linq xml parsing to create checkbox - c#

Hi I have a dictionary object implemented:
public partial class Window1 : Window
{
...
public Dictionary<string, string> Dictionarycheck = new Dictionary<string, string>();
public Window1()
{
Dictionarycheck.Add("Execbuildstartingmail", "Execbuildstartingmail");
Dictionarycheck.Add("Execbuildlastmail", "Execbuildlastmail");
...
}
}
then I have xml file to be parsed. I have more than 1 of such target to be parsed. I am supposed to parse the string of the attribute "if" so over here i would need "Execexample" as a string.
<project>
...
<target if="Execexample">
...
</target>
...
</project>
To do this parsing i tried to implement this code (which i'm not sure if it is correct):
private void xmlparsingButton_Click(object sender, RoutedEventArgs e)
{
XDocument xmlDoc = XDocument.Load(#"C:\Build.xml");
var abc = from target in xmlDoc.Descendants("target")
select (string)target.Attribute("if");
foreach(string target in abc)
{
// check this value whether if it exist in the dictionary.
// if not create a checkbox for it.
}
}
For the "foreach" statement i am supposed to do a check with the dictionary object. If there is no such value in dictionary, i will create a checkbox in my tabitem 5 and the name as the string (for example ExecexamplecheckBox).
how should i implement such a foreach function?
EDIT 1:
I forgot to add something. I need to write a text to a text file if these new checkboxes are checked. How do i do that? And they are required to write to a textfile that was written previously.

Does this solve the problem?
foreach(string target in abc)
{
//check this value whether if it exist in the dictionary. if not create a checkbox for it.
if (!Dictionarycheck.ContainsKey(target))
{
CheckBox chk = new CheckBox();
chk.Text = target;
SomePanel.Controls.Add(chk);
}
}

I can see two separate problems here:
1 - Data problem
Parse the xml file and extract the necessary data to a seperate list or dictionary.
2 - User interface
Generate a series of checkboxes based on the output of the data problem.
First you should separate the data problem away from the UI. In the foreach loop build a list of objects. The object should have at least a string property, for display, and a boolean property for the checkbox to bind to.
Second, on your UI create a ItemsControl (such as a ListBox) and bind its ItemsSource to the above list, then set the ItemTemplate to a DataTemplate containing a CheckBox
<ListBox ItemsSource="{Binding Path=SomeList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=DisplayString}" IsChecked="{Binding Path=Selected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
After the user has finished checking/unchecking the list, you will be left with a list upon which to write your text lines to a file.
There are several concepts you should learn when dealing with WPF, probably the hardest to get your head around is the seperation of concern between the UI and the data, and how the data layer can feed the UI to create elements for you. There should be no need to create new checkboxes in code using this technique.
Also, you will be better off not binding directly to a dictionary (although it is possible) but you should use ObservableCollection as your ItemsSource, it will handle change notification to the UI for you.

With the looks of it, as #Jackie mentioned, you could use HashSet. It won't make a difference except that it is the correct used of the right data structure in this case.
var filePath = "PathAndFileNameOfSomeFile.txt";
var hashset = new HashSet<string> { { "Execbuildstartingmail" }, { "Execbuildlastmail" } };
var xmlString = "<project><target if=\"Execexample\"></target><target if=\"Execbuildlastmail\"></target></project>";
var xDocument = XDocument.Parse(xmlString);
var attributeValues = from attrs in xDocument.Descendants("target").Attributes("if")
select attrs.Value;
foreach (var value in attributeValues)
{
var controls = this.Controls.Find(value, true);
//No checkbox with matching name found
if (controls.Length == 0)
{
if (!hashset.Contains(value))
{
var checkbox = new CheckBox();
checkbox.Parent = tabControl1.TabPages["tabPage2"];
checkbox.Text = value;
}
}
//checkbox with the given name was found AND is checked
else if (controls.Length == 1 && ((CheckBox)controls[0]).Checked)
{
using (var writer = File.AppendText(filePath))
{
writer.WriteLine(value);
}
}
}
After your Edit
I'm not clear about what you're saying in your edit, so I'll rephrase it...
While inthe foreach, if the checkbox exists and is checked, then you need to write some test to a text file. If they are not check there is nothing to do. If a checkbox does not exist, then add it to the tab is that correct?
I've updated the code to show you how to do three things:
Find Checkboxes in your form.
See if a checkbox is checked or not
Write some text to a text file.
even of the code I've provided does not do "Exactly" what you want I believe the various parts should help you get to your end goal. If not then please simply your question and provide very clear statements of where specifically you need help.

Related

Updating ListBox With New COM Port Data

I wish for my ListBox to update the old values with new values rather than simply adding more and more lines to the ListBox like it does at the moment. However, I'm not sure where to look to implement something that can handle this.
My current code looks like this:
private void DisplayText(string rawData)
{
textArduinoData.Text = rawData;
string[] sortedData = rawData.Split(';');
for (int i = 0; i < sortedData.Length; i++)
{
listPortData.Items.Add(sortedData[i].ToString());
}
}
Could someone please point me in the right direction to implementing this update feature? Any advice would be much appreciated.
You need to manage the process. It is easy in concept but depending on how much data is needed to be processed, it could get slow quickly. Steps
Create a specialized token class which implements to INotifyPropertyChanged.
Have an ObservableCollection hold the class items from #1. The observable collection notifies the ListBox when an item is added or removed. This will allow your code to add items one at a time. (Solves 1 problem)
To solve the next problem of data changing: Have a property named Text, on the class in #1 which will hold the data, provide a property change notification.
In the list box bind to the list of items created in step 1 and specify to bind to the Text. Use of a data template for the listbox will allow you to bind to the Text property of the list's instance.
Provide the heuristics/ smarts to read incoming data and find the associated data in the observable collection from step 2. When found change the Text property of the existing data to the new and the binding of that list item will change accordingly.
You could check if the ListBox contains the string using the IndexOf method and then update the existing string (or simply do nothing) or add a new one depending on whether you get an index other than the default value of -1 back:
private void DisplayText(string rawData)
{
textArduinoData.Text = rawData;
string[] sortedData = rawData.Split(';');
int index;
for (int i = 0; i < sortedData.Length; i++)
{
if ((index = listPortData.Items.IndexOf(sortedData[i])) == -1)
{
listPortData.Items.Add(sortedData[i]);
}
}
}

Using a Model With a ComboBox

I'm having trouble accessing the ID of a model I am adding to a ComboBox. At the moment I have a model, a preset, that contains an ID and Text. I create a list of presets through OleDB and then filter the list down before adding the contents to a ComboBox. Here is the code that does this;
var ps = new PresetService();
List<PresetModel> presetList = ps.HandleGetPresets();
List<PresetModel> filteredList = presetList.Where(filteredPreset => filteredPreset.PresetReferenceFoxPro == 3).ToList();
try
{
foreach (PresetModel preset in filteredList)
{
presetComboBox.Items.Add(preset.PresetText);
}
}
catch (Exception ex)
{
var hEs = new HandleExceptionService();
hEs.HandleException(ex.ToString());
return false;
}
Here you can see that I am adding the preset's text to the ComboBox. The issue comes however when someone wants to add a Company using one of the presets. In this case I am actually not interested in the text, I just want to add the preset's ID to the database against a company instead.
Using SelectedItem is not allowing me to access the ID, it returns a null exception. This is what I've triedl
var selectedPreset = presetComboBox.SelectedItem as PresetModel;
var presetIDToAdd = selectedPreset.PresetID;
I assume that this does not work because I have simply added the preset's Text value to the ComboBox and not it's ID. How can I add preset to a ComboBox, only display it's text but access it's ID when needed?
The problem here is that you are adding the PresetText to the ComboBox, not the PresetModel itself:
presetComboBox.Items.Add(preset.PresetText);
On top of that, you are then trying to cast the SelectedItem to a PresetModel, but it's actually the PresetText.
I would suggest using the following method.
Firstly, add the whole object to the ComboBox, like this:
presetComboBox.Items.Add(preset);
You can then define an ItemTemplate on your ComboBox to display the PresetText, or, to make things easier, just set the DisplayMemberPath:
<ComboBox ...
DisplayMemberPath="PresetText"/>
This will allow you to cast the SelectedItem to a PresetModel, but also still displaying the PresetText property in the ComboBox.

HOW TO work with a variable, that is saved in a string content (in C#, WPF)

I've got a for loop to read line by line of a stream reader. This works very well. Now I've problems with reading the file of the stream reader. It's a csv file that contains two columns with information. The first one (A) contains a C# element like a textbox or a label (just like "label_1_title"), which is declared in my project. The second one (B) contains a normal simple string like "Hello".
Now I want to convert the string content of column A to the real existing element. Example: I've got "label_1_title" written in column A and that's an element, that exists in my project. Now I want to use this element to (for example) change it's content to the content of the column B of that line.
public void SetSprachpaket(string Sprachpaket)
{
StreamReader StreamReader = new StreamReader(Sprachpaket, Encoding.UTF8);
string Inhalt = StreamReader.ReadLine();
for (int i = 1; Inhalt != null; i++)
{
var Items = Inhalt.Split(new Char[] { ';' });
object Element = Items[0].GetType(); // Convert the string content of Items[1] to the existing object
Element = Items[1]; // Take this existing object and give it the string content of Items[2]
Inhalt = StreamReader.ReadLine();
}
StreamReader.Close();
}
I hope you can help me. Thanks in advance. Kind regards.
EDIT:
object Element = Items[0].GetType(); // Get (let's say) the string "myString"
Element = Items[1]; // --> myString = ...
Please don't rely on internal naming within your project. You should have some kind of explicit mapping between elements and the column data.
Dump the CSV file into a Dictionary<string, string> of Column A => Column B. On the element containing all the elements you want to populate, set the DataContext to the dictionary. Define your labels like this:
<Label Content="{Binding [label_1_title]}" />
For more details, see Data Binding Overview. This will look up the dictionary key "label_1_title" and set the content to the value when the label loads.
In case you want to use additional data binding other than the label dictionary, I recommend you use a custom object for the DataContext, that stores the dictionary in a property such as LabelDictionary. In this case the binding should be:
<Label Content="{Binding LabelDictionary[label_1_title]}" />
Simply by using foreach
foreach(control x in this.Control)
{
if(x.Name == A) // For A means the name of the object
{ x.Text = B; } // For B means the string
}
This is not ideal, you'll need to play around with it, but somewhere in the direction you are looking for I think.
while (Inhalt != null)
{
switch (Items[0])
{
case "label"
element = new Label();
element.Text = Items[1];
break;
case "textbox"
element = new Textbox();
element.Text = Items[1];
break;
case "numericUpDown"
element = new NumericUpDown();
element.Value = Item[1];
break;
}
}
Edit : if you are not wanting to populate the form from scratch, use this.Controls.Find() to find the control with that name.
If the items[0] is the name of the control you can do this to find the Control
this.Controls.Find(items[0])
you might then have to cast it to the appropriate type depending on which property you wish to assign to
control.Text = items[1];
Assuming that the first column is the name of a field in the same class and not the name of a control you could do this
var field = this.GetType().GetField(items[0],
BindingFlags.Instance
| BindingFlags.NonPublic);
var control = (Control)field.GetValue(this,null);
control.Text = items[1];

How to add different items in a ListView

I have a ListView of this type:
<ListView x:Name="LView">
<ListView.View>
<GridView x:Name="GView"></GridView>
</ListView.View>
</ListView>
Initially I don't know how many columns there will be in my GridView, and I don't know what is the type of a single cell.
I need to have the possibility to add at run-time new columns and especially new rows whose cells contain different objects.
ListView has the property Items, so if I have a single column of string and Iwant to add a row i write:
LView.Items.Add("my string");
Now i want to add a new column:
GView.Columns.Add(new GridViewColumn());
A new column is added but the second cell of the first row contains the string "my string", why? I don't want this behavior but a new empty cell.
And especially if I want to add a new row with the first cell as String and the second cell as CheckBox what I have to do?
I tried:
LView.Items.Add("my second string", new CheckBox());
But, obviously, it does not work.
I tried to add a new Object:
public class obj
{
public string MyString{ get; set; }
public CheckBox MyCBox { get; set; }
}
LView.Items.Add(new obj());
But it dows not work because it display a string that contains (Namespace "+ obj"), and I don't know how many columns there will be, so I can't know how the obj class have to be initially.
What I have to do to solve my problems? Thanks.
I think you mix data items with ui elements
LView.Items.Add("my string");
will be presented as a textelement using default template
If you havent defined any data templates and want to explicitly add ui elements try this instead:
LView.Items.Add(new TextBlock { Text = "My string"});
However, I think GridView is suppoused to be populated with data (along with power of data bindings), not explicitly ui elements. Column Template should define how data is presented (in your case an checkbox or textelement).
See example http://www.c-sharpcorner.com/uploadfile/mahesh/gridview-in-wpf/

Retrieve DataItem from RepeaterItem after databinding (during an event handler)

I'm trying to find a way to to do something that I think must be possible but I'm just missing the point on - so hopefully someone can give me a bit of a nudge :)
I'm utilising databinding in ASP.NET (viewstate turned off - so using controlstate for a few things here and there) to render a repeater (repeaterPriceClasses) that has a repeater within each itemtemplate (repeaterPriceBands). Basically this renders a table out of some text and a dropdownlist in each cell.
I'm trying to find a way to enumerate the repeaterOuter inside the event handler of a button to give me a list of all of the originally bound elements that have now got a dropdownlist with a value of >0, along with what that value is.
public Dictionary<Price, int> SelectedPrices
{
get
{
var sel = new Dictionary<Price, int>();
foreach(RepeaterItem itemClass in repeaterPriceClasses.Items)
{
var repeaterPriceBands = itemClass.FindControl("repeaterPriceBands") as Repeater;
foreach(RepeaterItem itemBand in repeaterPriceBands.Items)
{
var context = (Price)itemBand.DataItem;
var listQty = itemBand.FindControl("listQty") as DropDownList;
if(listQty.SelectedValue.ToInt32() > 0)
{
sel.Add(context, listQty.SelectedValue.ToInt32());
}
}
}
return sel;
}
}
Now this fails because the itemBand.DataItem is always null after databinding has finished.
What technique should I use to get around this?
Hidden field with primary keys in it
(not ideal as can be abused and adds
weight to the page)
Lookup from
original cached data based on indexes
(just seems wrong)
or something
else/better...?
EDIT: Is there any more information that I could provide to help this get answered?
You can try adding extra attributes to your HTML elements in the ItemDataBound event when DataItem is available.
ddlSomething.Attributes["Attribute1"] = dataItemObject.PropertyName;
[Edit]
Apart from using attributes, you can render some array containing JSON objects like this, for each row.
var repeaterItemAttributes = [ { Prop1: val1, Prop2: val2, ... }, { ... }, ... ]
So, by using this, you won't have to render the attributes, and your code will be XHTML compliant too (tho its not required). You can then loop through the object array and read any property you like. You can also apply the light encryption you were talking about to the property values.
[/Edit]

Categories

Resources