C# Errors - due to optional parameters or something else? - c#

Here is my code:
public void RemovalWorker_Start(Applications app = null, Link link = null)
{
BackgroundWorker RemovalWorker = new BackgroundWorker();
RemovalWorker.DoWork += RemovalWorker_DoWork;
RemovalWorker.RunWorkerCompleted += RemovalWorker_RunWorkerCompleted;
RemovalWorker.WorkerSupportsCancellation = true;
RemovalWorker.RunWorkerAsync(arg);
}
private void RemovalWorker_DoWork(object sender, DoWorkEventArgs e)
{
var app = e.Argument.GetType();
if(app.Name == "Applications")
{
Applications RemovalAppliction = (Applications)e.Argument;
RemovalAppliction.RemoveApplication();
Dispatcher.Invoke(new Action(() =>
{
user.RemoveFromMyApps(user._MyApps[listBox_apps.SelectedIndex]);
}));
e.Result = "apps";
}
else
{
Link RemovalLink = (Link)e.Argument;
RemovalLink.RemoveLink();
Dispatcher.Invoke(new Action(() =>
{
user.RemoveFromMyLinks(user._MyLinks[listBox_links.SelectedIndex]);
}));
e.Result = "links";
}
}
I am trying to pass an optional parameter to my RemovalWorker. However, I receive the following errors:
Inconsistent Accessibility: parameter type 'Applications' is less accessible than method 'MainWindow.RemovalWorker_Start(Applications,Link)'
Inconsistent Accessibility: parameter type 'Link' is less accessible than method 'MainWindow.RemovalWorker_Start(Applications,Link)'
Why is this happening? Am I declaring my optional parameters incorrectly? How should I go about fixing this? The "Removal Worker" is instantiated via button click (hitting delete on a listbox item). The argument I would like to pass is whether it is an "Applications" object or a "Link" object.
The function is not 100% complete - I still need to declare what my argument will be, however, I currently can not compile.
Thanks All!

I guess your Applications and Link is your own class which is not a public class
So when you make public void RemovalWorker_Start the public in this line conflict with nonpublic of Applications and Link

Related

NEW TITLE -- The application called an interface that was marshalled for a different thread

After making the project simpler, I believe I identified the problem is actually a result the async marshalling.
UPDATE: I made the code simpler to try to figure out what was going on. So here is an update... The Observable collection is being populated on a new thread (async method). I tried moving the assigning of the ItemsSource to after the ObservableCollection is loaded as seen below
async void LoadAllData(object sender, EventArgs e)
{
if (sender != null)
{
App.GeoLocationComplete -= LoadAllData;
}
await ViewModelObjects.NearbyLocations.LoadLocationData();
lvPlaces.ItemsSource = ViewModelObjects.NearbyLocations.GBSLocationDetails;
}
The definition for the data load method is a follows:
public async Task LoadLocationData()
{....}
When I run this code I get the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I know what is causing the error (the data was loaded on a thread other than the UI thread) but I don't know how to fix it. Suggestions?
UPDATE UPDATE: So I believe I have identified the root cause of the problem but have not figured out how to fix it. I started by simplifying my code as follows and it worked.
public nearbyplaces()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
LoadAllData(null, null);
}
void LoadAllData(object sender, EventArgs e)
{
lobj_Places = new ObservableCollection<GBSLocationDetail>()
{
new GBSLocationDetail()
{
Title = "Location 1",
Distance = "20 Miles",
AddInfo = "Something Else",
AttributesTexts="Gay, Bar, Dance"
}
};
lvPlaces.ItemsSource = lobj_Places;
}
HOWEVER, what I need is for the LoadAllData method to be called once I have the GPS location from the device. So in my App.XAML.cs I have the following delegate event declared:
public static Plugin.Geolocator.Abstractions.IGeolocator gobj_RealGeoCoordinator;
public static event GeoLocationCompleteEventHandler GeoLocationComplete;
public static bool gb_WaitingForLocation = true;
Then I have the following code call the event once I get the location back from the device:
private async void ProcessStartupandResume()
{
if (gobj_RealGeoCoordinator == null)
{
gobj_RealGeoCoordinator = CrossGeolocator.Current;
ViewModelObjects.AppSettings.CanAccessLocation = App.gobj_RealGeoCoordinator.IsGeolocationEnabled;
if (!ViewModelObjects.AppSettings.CanAccessLocation)
{
await MainPage.DisplayAlert(ResourceStrings.GetValue("NoLocationServicesTitle"), ResourceStrings.GetValue("NoLocationServicesMessage"), ResourceStrings.GetValue("OKButtonText"));
}
//Only add the events if the object has to be created.
gobj_RealGeoCoordinator.PositionChanged += gobj_RealGeoCoordinator_PositionChanged;
gobj_RealGeoCoordinator.PositionError += (sender, e) =>
{
ProcessException(new Exception(e.Error.ToString()));
};
}
//Set this to null to trigger the first check
ib_GPSReenabled = null;
if (gobj_RealGeoCoordinator.IsListening)
await gobj_RealGeoCoordinator.StopListeningAsync();
gobj_RealGeoCoordinator.DesiredAccuracy = 50;
await gobj_RealGeoCoordinator.StartListeningAsync(10000, 20);
}
private static void gobj_RealGeoCoordinator_PositionChanged(object sender, PositionEventArgs e)
{
var pos = e.Position;
ViewModelObjects.AppSettings.Latitude = pos.Latitude;
ViewModelObjects.AppSettings.Longitude = pos.Longitude;
if (gb_WaitingForLocation)
{
gb_WaitingForLocation = false;
GeoLocationComplete?.Invoke(new object() , null);
}
}
Then in my page I subscribe to the GeoLocationComplete event using the LoadAllData method as seen below. Even when I use a local object and try to set the ItemsSource for the ListView in the code when executed as a result of the event being raised, I receive the error. See code below which subscribed to the event:
public nearbyplaces()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
if (App.gb_WaitingForLocation)
App.GeoLocationComplete += LoadAllData;
else
LoadAllData(null, null);
}
Any suggestions on how to fix this?
OK so I figured it out. I needed to invoke the event on the main thread and I did that with the following code:
Device.BeginInvokeOnMainThread(() =>
{
GeoLocationComplete?.Invoke(new object(), null);
});
After inserting this code, the error was gone. Changing the code back to simply
GeoLocationComplete?.Invoke(new object(), null);
cause the error to occur again. Thus I believe this resolved my problem. Hope this helps someone else. :)

"Cross-thread operation is not valid" exception when trying to get value of comboBox

"Cross-thread operation is not valid" exception
I experienced this exception many times, but all those times I was setting the value of a control. That time I solved using a function called SetControlPropertyThreadSafe(), which was suggested by someone on stackoverflow.com only. But this time I am getting this exception when I am trying to get the value of comboBox. Here is the code:
string cat;
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Trim().Length > 20)
{
System.Threading.Thread t = new System.Threading.Thread(addQuickTopic);
t.Start();
}
else
MessageBox.Show("The length of the title must be greater than 20", "Title length invalid", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
public string tTitle="";
void addQuickTopic()
{
Global.SetControlPropertyThreadSafe(button1, "Text", "Working...");
Global.SetControlPropertyThreadSafe(button1, "Enabled", false);
Topic t = new Topic();
t.setTitle(textBox1.Text);
t.setDescription(" ");
t.setDesID(Global.getMd5Hash(Common.uid+DateTime.Today.ToString()+DateTime.Today.Millisecond.ToString()));
t.setUsrID(Common.uid);
t.setReplyToID("");
t.setConDate("0");
cat = CategoryList.SelectedValue.ToString();
As you can see I am getting the textBox1.Text directly without applying any thread safe operation. But at the last line when trying to fetch comboBox's selected value, I am getting this exception. So can any one please suggest me what to do in this situation? Following is the code for my thread safe function for setting control's value:
public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
{
try
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
}
}
catch (Exception)
{ }
}
Should I need to create a similar get function? Or any other solution available?
Right now, you're getting the value from the TextBox, via: t.setTitle(textBox1.Text);. This will also fail.
Should I need to create a similar get function? Or any other solution available?
Yes, you need an option for get. I would recommend not using reflection and text, however, and rework this to use generic methods and lambdas.
public static void SetControlPropertyThreadSafe<T>(T control, Action<T> action) where T : Control
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
This would allow you to write this strongly typed, ie:
Global.SetControlPropertyThreadSafe(button1, b => b.Text = "Working...");
You can also make a strongly typed get method:
public static U GetControlPropertyThreadSafe<T,U>(T control, Func<T,U> func) where T : Control
{
if (control.InvokeRequired)
{
return (U)control.Invoke(func, new object[] {control});
}
else
{
return func(control);
}
}
Which then lets you write:
t.setTitle(Global.GetControlPropertyThreadSafe(textBox1, t => t.Text));
You could also use the same methods for getting and setting the combo box items.
You need a similar get function, which calls Invoke if required.
This depends from the threading model of the GUI, which states that all the methods that interact with the GUI should only be called by the GUI thread.
It's not important if you're trying to read, to write, or do anything else. If you're not on the GUI thread, you must use Invoke.

Parameter count mismatch. - Threading

I am trying to create a multi-client / server chat small application using WPF but I have some problems. Unfortunately when I press the Connect button the program crashes.
Well, I done that so far with the client program(with the thread):
public delegate void UpdateText(object txt);
I got that method:
private void UpdateTextBox(object txt)
{
if (msg_log.Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new UpdateText(UpdateTextBox),txt);
}
else
{
msg_log.Dispatcher.Invoke(new UpdateText(UpdateTextBox), txt);
}
}
And I am using a Button_Click event to connect to the server like that:
private void connect_Click(object sender, RoutedEventArgs e)
{
if ((nick_name.Text == "") || (ip_addr.Text == "") || (port.Text == ""))
{
MessageBox.Show("Nick name, IP Address and Port fields cannot be null.");
}
else
{
client = new Client();
client.ClientName = nick_name.Text;
client.ServerIp = ip_addr.Text;
client.ServerPort = port.Text;
Thread changer = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(UpdateTextBox));
changer.Start();
client.OnClientConnected += new OnClientConnectedDelegate(client_OnClientConnected);
client.OnClientConnecting += new OnClientConnectingDelegate(client_OnClientConnecting);
client.OnClientDisconnected += new OnClientDisconnectedDelegate(client_OnClientDisconnected);
client.OnClientError += new OnClientErrorDelegate(client_OnClientError);
client.OnClientFileSending += new OnClientFileSendingDelegate(client_OnClientFileSending);
client.OnDataReceived += new OnClientReceivedDelegate(client_OnDataReceived);
client.Connect();
}
}
Please note that the OnClient* events are like private void client_OnDataReceived(object Sender, ClientReceivedArguments R) { UpdateTextBox(R.ReceivedData); }
So these events should write some text like "Connected" into the msg_log TextBox
PS. The txt object used to be a string variable but I change it since ParameterizedThreadStart only accepts objects as parameters as I know.
Any help would be greatly appreciated!
Thanks in advance,
George
EDIT: Edited the UpdateTextBox method as Abe Heidebrecht suggested.
There are a few things wrong with your Invoke calls.
You don't need to create an object array to pass the parameters.
Passing DispatcherPriority.Normal is redundant (Normal is default).
You aren't passing any parameters to the second Invoke method (which is probably where your error is happening).
It should look like this:
private void UpdateTextBox(object txt)
{
if (msg_log.Dispatcher.CheckAccess())
{
Dispatcher.Invoke(new UpdateText(UpdateTextBox), txt);
}
else
{
msg_log.Dispatcher.Invoke(new UpdateText(UpdateTextBox), txt);
}
}
EDIT For StackOverflowException
This will cause a StackOverflowException because you are calling your method in an infinite loop. This is happening because your method is simply calling itself over and over again.
What Dispatcher.Invoke does is invoke the passed delegate on the thread that owns the Dispatcher. Just because the msg_log may have a different dispatcher, when you were calling UpdateTextBox, you were passing a delegate to the current method, which causes the infinite loop.
What you really need to do is call a method on the msg_log object, like so:
private void UpdateTextBox(object txt)
{
if (msg_log.Dispatcher.CheckAccess())
{
if (txt != null)
msg_log.Text = txt.ToString();
}
else
{
msg_log.Dispatcher.Invoke(new UpdateText(UpdateTextBox), txt);
}
}

Can't get the first method name in a multicast delegate

I have this code:
public void AddMenuRow(FuncInvoker i_FuncToAdd) // add a row to menu.
{
if (d_Lines == null)
{
d_Lines = new FuncInvoker(i_FuncToAdd);
}
else
{
d_Lines += i_FuncToAdd;
}
}
for adding methods to the invoke list.
And now I want to print the name of each method to the console, so I made this:
public void Show()
{
int count = 1;
string name = null;
Console.WriteLine(m_Title);
foreach (FuncInvoker list in d_Lines.GetInvocationList())
{
name = list.Method.Name;
Console.WriteLine((count++) + ". " + name);
}
}
The problem is in the first method name, which always prints "invoke" for some reason.
The next methods in the delegate link work fine.
Can someone help me with this? I have tried everything.
In this line:
d_Lines = new FuncInvoker(i_FuncToAdd);
...you're actually creating a new delegate instance that wraps the original delegate. The method-target of this new delegate will be the Invoke method of the original delegate (assuming it's unicast), which explains the behaviour you're observing.
The obvious workaround is to not use a wrapper and just copy a reference to the original delegate to the variable:
d_Lines = i_FuncToAdd;
But you might as well do away with your 'special-case' branch completely and just do (assuming the argument can't be null):
public void AddMenuRow(FuncInvoker i_FuncToAdd)
{
d_Lines += i_FuncToAdd;
}
This will work fine since Delegate.Combine (which is what the += syntax becomes) is speced to return a reference to the second delegate if the first delegate is null, rather than throwing an exception.

passing values between forms (winforms)

Wierd behaviour when passing values to and from second form.
ParameterForm pf = new ParameterForm(testString);
works
ParameterForm pf = new ParameterForm();
pf.testString="test";
doesn't (testString defined as public string)
maybe i'm missing something? Anyway I'd like to make 2nd variant work properly, as for now - it returns null object reference error.
Thanks for help.
Posting more code here:
calling
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(doc.GetElementById(ParametersButton.Tag.ToString()));
pf.ShowDialog(this);
pf.test = "test";
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
};
definition and use
public partial class ParameterForm : Form
{
public string test;
public XmlElement node;
public delegate void ParameterSubmitResult(object sender, XmlElement e);
public event ParameterSubmitResult Submit;
public void SubmitButton_Click(object sender, EventArgs e)
{
Submit(this,this.node);
Debug.WriteLine(test);
}
}
result:
Submit - null object reference
test - null object reference
pf.ShowDialog(this); is a blocking call, so pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit); is never reached: switch the order.
Submit(this,this.node); throws a null object reference because no event is assigned to it (see above). Generally, you should always check first: if (Submit != null) Submit(this,this.node);
You should change ``pf.ShowDialog(this);topf.Show(this);` so that your main form isn't disabled while your dialog box is open, if that's what you want, or use the model below (typical for dialog boxes.)
I'm not sure what pf_Submit is supposed to do, so this might not be the best way to go about it in your application, but it's how general "Proceed? Yes/No" questions work.
Button ParametersButton = new Button();
ParametersButton.Click += delegate
{
ParameterForm pf = new ParameterForm(testString);
pf.ShowDialog(this); // Blocks until user submits
// Do whatever pf_Submit did here.
};
public partial class ParameterForm : Form
{
public string test; // Generally, encapsulate these
public XmlElement node; // in properties
public void SubmitButton_Click(object sender, EventArgs e)
{
Debug.WriteLine(test);
this.Close(); // Returns from ShowDialog()
}
}
When you want to use your second variant, you have to use a getString()-Method, where you can put the e.g. "testString". The way you wrote it, "testString" should be a method (and got brackets).
EDIT (a bit more precise):
You could write:
pf.getString(testString);
, if "pf" is an instance of your own class, otherwise you had to look up, whether you can retrieve a String in this class.
the thing was in line order :)
pf.Submit += new ParameterForm.ParameterSubmitResult(pf_Submit);
and
pf.Test = "test";
should have been set before
pf.ShowDialog(this);
my mistake thingking that parameter can be passed after 2nd form was displayed
thnx for answers

Categories