Return values from XDocument with events - c#

I wrote a class ReasXML the basically read only XML files. You see I worked withe events.. Now the problem is I want to use the result of lsTags or I want the values of my XML file. The basic thought to do that: make the function a return type List for the method LoadXMLFile and XMLFileLoaded. But I receive an error that, has the wrong return type.
Can someone help me with this of give me an example with events how to return variables?
Thanks in advance!
public void LoadXMLFile() {
WebClient xmlClient = new WebClient();
xmlClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(XMLFileLoaded);
xmlClient.DownloadStringAsync(new Uri("codeFragments.xml", UriKind.RelativeOrAbsolute));
}
protected void XMLFileLoaded(object sender, DownloadStringCompletedEventArgs e) {
if (e.Error == null) {
string xmlData = e.Result;
XDocument xDoc = XDocument.Parse(xmlData);
var tagsXml = from c in xDoc.Descendants("Tag") select c.Attribute("name");
List<Tag> lsTags = new List<Tag>();
foreach (string tagName in tagsXml) {
Tag oTag = new Tag();
oTag.name = tagName;
var tags = from d in xDoc.Descendants("Tag")
where d.Attribute("name").Value == tagName
select d.Elements("oFragments");
var tagXml = tags.ToArray()[0];
foreach (var tag in tagXml) {
CodeFragments oFragments = new CodeFragments();
oFragments.tagURL = tag.Attribute("tagURL").Value;
oTag.lsTags.Add(oFragments);
}
lsTags.Add(oTag);
}
//List<string> test = new List<string> { "a","b","c" };
lsBox.ItemsSource = lsTags;
}
}

Event handlers can be any delegate type, not just EventHandler.
If you want to return a result, simply change the event to use
Func<CustomEventArgs, YourReturnType>
Here is some sample code:
using System;
class Program
{
public class Ev
{
public int? RaiseSomeEvent()
{
if (SomeEvent != null)
{
return SomeEvent();
}
return null;
}
public event Func<int> SomeEvent;
}
static void Main(string[] args)
{
var ev = new Ev();
ev.SomeEvent += ev_someEvent1;
ev.SomeEvent += ev_someEvent2;
int? value = ev.RaiseSomeEvent();
Console.WriteLine(value.HasValue ? value.Value.ToString() : "(null)");
}
static int ev_someEvent1()
{
return 5;
}
static int ev_someEvent2()
{
return 6;
}
}
The output of this code:
6
Multiple event handlers
Notice that you only get the last value returned by all of the event handlers.
If you want to handle multiple return values in a somewhat event-like fashion, you may want to check out the Visitor Design Pattern instead. If you use this pattern, you would have to create a visitor adapter (or visitor adapters) that has the Accept methods on it.

Related

C# custom add in a List

I have the following list of strings :
var files = new List<string> {"file0","file1","file2","file3" };
I would like to be able to add new files to this list, but if the inserted file is present in the list, I would like to insert custom value that will respect the following format $"{StringToBeInserted}"("{SomeCounter}
For instance : try to add "file0" and "file0" is already I would like to insert "file0(1)". If I try again to add "file0" ... I would like to insert with "file0(2)" and so on ... Also, I would like to provide a consistency, for instance if I delete "file0(1)" ... and try to add again "item0" ... I expect that "item0(1)" to be added. Can someone help me with a generic algorithm ?
I would use a HashSet<string> in this case:
var files = new HashSet<string> { "file0", "file1", "file2", "file3" };
string originalFile = "file0";
string file = originalFile;
int counter = 0;
while (!files.Add(file))
{
file = $"{originalFile}({++counter})";
}
If you have to use a list and the result should also be one, you can still use my set approach. Just initialize it with your list and the result list you'll get with files.ToList().
Well, you should create your own custom class for it, using the data structure you described and a simple class that includes a counter and an output method.
void Main()
{
var items = new ItemCountList();
items.AddItem("item0");
items.AddItem("item1");
items.AddItem("item2");
items.AddItem("item0");
items.ShowItems();
}
public class ItemCountList {
private List<SimpleItem> itemList;
public ItemCountList() {
itemList = new List<SimpleItem>();
}
public void DeleteItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null) {
item.Count--;
if (item.Count == 0)
itemList.Remove(item);
}
}
public void AddItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null)
item.Count++;
else
itemList.Add(new SimpleItem {
Value = value,
Count = 1
});
}
public void ShowItems() {
foreach (var a in itemList) {
Console.WriteLine(a.Value + "(" + a.Count + ")");
}
}
}
public class SimpleItem {
public int Count {get; set;}
public string Value {get; set;}
}

Add objects to arraylist and read them

I'm trying to add objects of DataPerLabel to my Arraylist allData, following the code of DataPerLabel:
class DataPerLabel
{
public String labelName;
public String labelAdress;
public String dataType;
public DataPerLabel(String labelName, String labelAdress, String dataType)
{
this.labelName = labelName;
this.labelAdress = labelAdress;
this.dataType = dataType;
}
public String getLabelName()
{
return labelName;
}
public String getLabelAdress()
{
return labelAdress;
}
public String getDataType()
{
return dataType;
}
}
In the following code I try to add the objects of DataPerLabel to my arraylist:
submitButton.Click += (sender, args) =>
{
String label = textboxLabel.Text;
String adress = textboxAdress.Text;
String dataType = "hey";
if (buttonsLabelBool.Checked)
{
dataType = "Bool";
}
else if (buttonsLabelReal.Checked)
{
dataType = "Real";
}
else if (buttonsLabelInt.Checked)
{
dataType = "Int";
}
allData.Add(new DataPerLabel(label, adress, dataType));
};
And finally I try to read out the arrayList by displaying it in a textbox, see the following code:
private void test()
{
Button btn = new Button();
btn.Location = new Point(500,500);
btn.Text = "test";
btn.Click += (sender, args) =>
{
foreach (var item in allData)
{
//Display arraylist per object here
//Something like : item.getLabelName();
}
};
}
I'm not sure what I'm doing wrong, hope you can help!
ArrayList stores a list of System.Object. You need to cast the object back to DataPerLabel as follows:
foreach (var item in allData)
{
((DataPerLabel)item).getLabelName();
}
Alternatively, you could specify the data type in the foreach instead of var as Jakub DÄ…bek pointed out in the comment as follows:
foreach (DataPerLabel item in allData)
{
item.getLabelName();
}
A better approach would be to use generic list/collection List<DataPerLabel> to store the data so that the casting can be avoided.
Yiu should use a List<T> instead of ArrayList. This way every item in your list has the right type already and you can access the members:
foreach (DataPerLabel item in allData)
{
item.GetLabelItem();
}
This assumes allData is defined like this:
allData = new List<DataPerLabel>();
instead of allData = new ArrayList()
If you really have to use an ArrayList than you should cast your item to the actual type. The code above actually does this allready. However you could also use this:
foreach (var item in allData)
{
((DataPerLabel)item).GetLabelItem();
}

Give out parameter on event in C#

I want to give an out parameter on event but it doesn't work
like this
error :Cannot use parameter out in anonyme method or lambda expression
public void CallMyMethod()
{
//{... code here`removed...just for initialize object}
int? count = null;
MyMethod(myObject,count);
}
public static void MyMethod(AnObject myObject,out int?count)
{
//{... code removed...}
IEnumerable<AnAnotherObject> objects = myObject.GetAllObjects();//... get objects
count = (count == null) ? objects.Count() : count;
MyPopup popup = CreateMypopup();
popup.Show();
popup.OnPopupClosed += (o, e) =>//RoutedEventHandler
{
if (--count <= 0)
{
Finished();//method to finish the reccursive method;
}
else
{
MyMethod(myObject, out count);
}
};
}
The standard way to pass parameters to event handler is to define your own class
public class MyObjectArgs: EventArgs
{
MyObject myObj {get;set}
int? Count {get; set;}
}
and then pass the same instance of this class down the recursive calls
public void CallMyMethod()
{
//{... code here`removed...just for initialize object}
MyObjectArgs args = new MyObjectArgs();
args.Count = null;
args.myOby = myObject;
MyMethod(args);
}
public static void MyMethod(MyObjectArgs args)
{
//{... code removed...}
IEnumerable<AnAnotherObject> objects = args.myObj.GetAllObjects();//... get objects
args.Count = (args.Count == null) ? objects.Count() : args.Count;
MyPopup popup = CreateMypopup();
popup.Show();
popup.OnPopupClosed += (o, e) =>//RoutedEventHandler
{
if (--args.Count <= 0)
{
Finished();//method to finish the reccursive method;
}
else
{
MyMethod(args);
}
};
}
Also the line where you count the objects seems to be incorrect, perhaps you want this ?
args.Count = (args.Count == null) ? objects.Count() : args.Count + objects.Count();

Await on item being present in a list

I'm currently looking for a way to monitor a list for an item to be added (although it may already be in the list) with a certain ID. The below example demonstrates what I want to do, however I was hoping there would be a neater approach to this.
var list = new List<string>();
var task = new Task<bool>(() =>
{
for (int i = 0; i < 10; i++)
{
if (list.Contains("Woft"))
return true;
Thread.Sleep(1000);
}
return false;
});
I would appreciate any suggestions, and can elaborate if required.
EDIT: I'm not sure how I would use a CollectionChanged handler for this, still not sure I'm describing the problem well enough, here is a more complete example.
class Program
{
private static List<string> receivedList = new List<string>();
static void Main(string[] args)
{
var thing = new item() { val = 1234, text = "Test" };
var message = new message() { id = "12", item = thing };
//This would be a message send
//in the receiver we would add the message id to the received list.
var task2 = new Task(() =>
{
Thread.Sleep(2000);
receivedList.Add(message.id);
});
task2.Start();
var result = EnsureReceived(thing, message.id);
if (result == null)
{
Console.WriteLine("Message not received!");
}
else
{
Console.WriteLine(result.text + " " + result.val);
}
Console.ReadLine();
}
//This checks if the message has been received
private static item EnsureReceived(item thing, string id)
{
var task = new Task<bool>(() =>
{
for (int i = 0; i < 10; i++)
{
if (receivedList.Contains(id))
return true;
Thread.Sleep(1000);
}
return false;
});
task.Start();
var result = task.Result;
return result ? thing : null;
}
}
class message
{
public string id { get; set; }
public item item { get; set; }
}
class item
{
public string text { get; set; }
public int val { get; set; }
}
It sounds like what you want to do is use an ObservableCollection and subscribe to the CollectionChanged event. That way, you'll have an event fire whenever the collection is modified. At that point, you can check if the collection has the element you're expecting.
Even better, the CollectionChanged event handler will receive NotifyCollectionChangedEventArgs, which contains a NewItems property.
See MSDN for more information.
You can implement own type, what intercept all operations to add items and exposes ReadOnlyList. Then create an event ItemAdded with added item value (you will have to rise up multiple events when adding a collection obviously). Subscribe to it, check, do whatever.

Why does this not work?

I have a class which will act as variables to store data from textboxes:
public class Business
{
Int64 _businessID = new Int64();
int _businessNo = new int();
string _businessName;
string _businessDescription;
public Int64 BusinessID
{
get { return Convert.ToInt64(_businessID.ToString()); }
}
public int BusinessNo
{
get { return _businessNo; }
set { _businessNo = value; }
}
public string BusinessName
{
get { return _businessName; }
set { _businessName = value; }
}
public string BusinessDescription
{
get { return _businessDescription; }
set { _businessDescription = value; }
}
I then have the code to store the data from the textbox into a session and into a list (there can be many businesses uploaded to the database at one time) - database irrelevent for now. I then want to display the list of businesses stored into the session onto the gridview: (b = class business)
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
businessCollection.Add(b);
GridView1.DataSource = GetBusiness();
GridView1.DataBind();
}
It doesn't seem to add the list to the gridview, can someone help?
Debug your code and ensure that if (Session["Business"] != null) actually evaluates to true.
If it is false then you are adding to a list that is never returned from GetBusinesss
Without any more information you can rewrite it like this:
List<Business> businessCollection = new List<Business>();
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
return businessCollection;
else
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
I personally wouldn't do it like this, as it seems like you need an assignment to Session["Business"] but I don't want to change the logic of your code.
Update
I wanted to update this with what I think you wanted to accomplish.
protected List<Business> GetBusinesses()
{
if (Session["Business"] == null)
Session["Business"] = new List<Business>();
return (List<Business>)Session["Business"];
}
protected void btnRow_Click(object sender, EventArgs e)
{
Business b = new Business();
b.BusinessNo = Convert.ToInt32(txtBNo.Text);
b.BusinessName = txtBName.Text;
b.BusinessDescription = txtBDesc.Text;
var currentCollection = GetBusinesses();
currentCollection.Add(b);
GridView1.DataSource = currentCollection;
GridView1.DataBind();
}
It seems you are not assigning anything to Session["Business"]
There's a very strong chance that you're problem is caused by the fact that you are referencing the Business List object inconsistently. You've created an accessor for this object, so use it everywhere.
This:
if (Session["Business"] != null)
businessCollection = (List<Business>)Session["Business"];
Should be:
var businessCollection = GetBusiness();
Note the use of var: I suspect defining businessCollection as a member variable is part of the problem. In any case it is bad design if your intent is to store the list in the session. So I would also remove the member declaration for businessCollection and stick with a locally scoped variable.

Categories