Loading an XML file in C# win forms - c#

In my program, the user can save the settings with a button. The store works. Loading the settings does not work quite right. I have a problem when loading the rule. I have several rules as a list. I dont know how to specify the index. Can anybody help me please?
Methods to save/load the settings:
private void SaveUserConfigButton_Click(object sender, EventArgs e)
{
var userConfig = new UserConfig();
userConfig.RandomPopulation = (int)_probability;
userConfig.Rule = _gameOfLife.NextGenerationRule.RuleName;
userConfig.Speed = _timer.Interval;
userConfig.UseBoundary = _gameOfLife.UseBoundary;
SaveUserConfig(userConfig);
}
private void MainForm_Load(object sender, EventArgs e)
{
var userConfig = LoadUserConfig(_path);
InputRandomPopulationNumbericUpDown.Value = userConfig.RandomPopulation;
SelectRulesComboBox.SelectedItem = _rules[5]; // <-- here is the problem
SpeedTrackBar.Value = userConfig.Speed;
BoundaryCheckBox.Checked = userConfig.UseBoundary;
}
My english is not so good, I hope it is understandable.

Assuming userConfig.Rule is the name of the rule you want selected in the SelectRulesComboBox and each instance of a rule has a property named Name what you need to do is find the index of userConfig.Rule within the _rules collection.
If _rules is a List<T> then you can use the FindIndex method:
SelectedRulesCombobox.SelectedIndex = _rules.FindIndex(r => r.Name == userConfig.Rule);
Otherwise, you can just project each rule alongside its index within _rules collection and get the first one that has Name == userConfig.Rule:
SelectedRulesCombobox.SelectedIndex = _rules.Select((rule, index) => new
{
Rule = rule,
Index = index
})
.First(x => x.Rule.Name == userConfig.Rule)
.Index;
Keep in mind though that the code above will throw an exception if no rule was found with Name == userConfig.Rule.

Why not use datatable & WriteXml and ReadXml ?
void writeResults()
{
DataTable dt = new DataTable();
dt.Columns.Add("configID");
dt.Columns.Add("configValue");
//Other code you want to add
//Then add row for each setting
Datarow r = dt.NewRow();
r["configID"]= "Speed"; //e.g. Speed
r["configValue"]=_timer.Interval.ToString();
dt.Rows.Add(r);
// snip
//then save datatable to file
dt.TableName="UserConfigs";
dt.WriteXml(#"filename_goes_here");
}
To read settings from file is even easier :
void readSettings()
{
DataTable dt = new DataTable();
dt.ReadXml(#"filename_goes_here");
for(int i = 0; i < dt.Rows.Count; i++)
{
switch(dt.Rows[i][0])
{
case "Speed":
try
{
_timer.Interval=Int32.Parse(dr.Rows[i][1]);
}
catch
{
// we've got a problem !
}
break;
default:break;
}
}
}
Edit: That's not optimal way, but it can get you started. Always try/catch every single block where you're validating data from xml - never trust user input, Nuff said.

Related

Is it possible to pass or replace part of control name?

first of all I would like you to brace yourselves.
I have started programming only around 6 weeks ago. I do apologize if anything facepalm worthy follows.
I was wondering if it is possible to pass only part of a control name?
This is the idea:
I have two teams, Class is set up like this:
public class Team
{
int _established; //could be string
string _name;
double _best
}
//Snip
I have 2 sets of forms, both identical for input, but the only difference are the names:
//Team 1
team1Year.Text = "";
team1Name.Text = "";
team1Numericupdown.Value = 0;
//Team 2
team2Year.Text = "";
team2Name.Text = "";
team2Numericupdown.Value = 0;
So, it is essentially the same. I was wondering if it is possible to create a method that would do the following:
//Snip
Team team1 = new Team()
Team team2 = new Team()
//Not specific to problem, values can be anything
team1.estYear = 2008;
team1.Name = "Teamname1";
team1.Best = 137.8;
team2.estYear = 2009;
team2.Name = "GenericName";
team2.Best = 134.3;
private void HowToMethod(string tm, Team values)
{
//so it would become like this - replace team1 and team2 with an argument
tmYear.Text = values;
tmName.Text = values;
tmNumericupdown.Value = values;
}
And in practice:
HowToMethod(team1, team1.estYear)
//Output: team1Year.Text = 2008;
HowToMethod(team1, team1.Name)
//Output: team1Name.Text = Teamname1;
HowToMethod(team2, team2.Best)
//Output: team2Numericupdown.Value = 134.3;
This is not mandatory, I was just wondering if such thing is possible. I am fine with not having solution to this. There are different control/forms present, not all are the same, but they belong to the same "group" by name.
I hope it was not too confusing.
EDIT1:
So i have tried to be less confusing. Again, I do apologize for the facepalm. English is also not my first language, which also doesn't help. Anyways, here it goes:
This works, but to me it looks like repetitive code.
I was wondering if there is a simpler solution
...that is not an advanced topic
private void button1_Click(object sender, EventArgs e)
{
//Just one example. It's about manipulating Forms/Controls via code
//Lock Team1's controls
team1Year.Enabled = false;
team1Name.Enabled = false;
team1NumericUpDown.Enabled = false;
//Lock Team 2's Controls
team2Year.Enabled = false;
team2Name.Enabled = false;
team2NumericUpDown.Enabled = false;
}
private void button2_Click(object sender, EventArgs e)
{
//Another button unlocks
//Unlock Team1's controls
team1Year.Enabled = true;
team1Name.Enabled = true;
team1NumericUpDown.Enabled = true;
//Unlock Team 2's Controls
team2Year.Enabled = true;
team2Name.Enabled = true;
team2NumericUpDown.Enabled = true;
}
Ok, so the above works - manually pick the controls we want to disable
Can it be something like this:
Apologies if not using proper object types
private void MyMethod(int X, bool isItEnabled)
{
//This feels so, so wrong
Control ctrl = sender as Control;
team{X}Year.Enabled ;
team{X}Name.Enabled = {isItEnabled};
team{X}NumericUpDown.Enabled = {isItEnabled};
}
//In action:
private void button1_Click(object sender, EventArgs e)
{
MyMethod(1, false)
//Would mean/become:
team{1}Year.Enabled = {false};
team{1}Name.Enabled = {false};
team{1}NumericUpDown.Enabled = {false};
//Or if the method was implemented differently:
private void MyMethod(string prefix, bool isItEnabled)
{
{prefix}Year.Enabled ;
{prefix}Name.Enabled = {isItEnabled};
{prefix}NumericUpDown.Enabled = {isItEnabled};
}
//In action:
MyMethod(team2, true)
//Would mean:
{team2}Year.Enabled = {true};
{team2}Name.Enabled = {true};
{team2}NumericUpDown.Enabled = {true};
So the arguments passed could replace only part of the control name
if they have a shared sequential name
In conclusion:
** Is it possible to introduce a variable to replace part of code for modularity**:
//Making the idea into a method would be secondary
could become:
X = "team1";
Properties.Settings.Default.{X}Totals;
{X}Year.Text = "";
{X}Name.Text = "";
{X}Whatever.Text = "";
{X}NumericUpDown.Value = 135;
I know it works with Classes:
private void TeamStuff(Team teamID, string value1, string value2)
{
teamID.Year = value;
teamID.Name = value;
//...etc
}
//then
TeamStuff (team1, "sample", "GenericName");
//becomes
team1.Year = "sample";
team1.Name = "GenericName";
I really dont know what you are trying to do, and i guess that there may be better ways to do it,but to answer your specific question, here is the implementation of HowToMethod
private void HowToMethod(string tm, Team values)
{
var yearControlName = tm + "Year";
foreach(var ctrl in Controls)
{
if (ctrl is TextBox)
{
var tb = (ctrl as TextBox);
if (tb.Name == yearControlName)
tb.Text = values.EstYear.ToString();
}
}
}
Its only for showing the EstYear but is enough for you to try yourself and implement the rest,
P.S. Surely this code can be shorter with Linq but for sake of simplicity as the OP is new programmer lets keep it this way.
Edit
Now things are more clarified, the answer is no, u cant do that the way you did it, if you want to select a control based on a variable value (Which is fine in certain circumstances) , thats fine, but you have to do somthing like I showed in above code, i.e. iterate through all controls and check if the name is equal to your desire.
The fact is your code goes through a compile progress and for compiler the acceptable name of a control for example, is the actuall text you written there, not a combination of variables that may produce the same name, why? Because variables dont get their value until you run your code and that is after you compiled your code and without correct name compiler fails.

ListBoxView populated using Linq based on TimePicker Value

Using WPF C#
Have a TimePicker from Xceed.WPF.Toolkit called TimePicker, want to be able to filter the files that are loaded to a listbox based on the last write time of a file
Question: I would like to limit the files listed based on the logTime variable using Linq and where the files have to match the logTime
Code for population of list box currently
private void LoadLogsNoDate(string ldate, string ext)
{
string[] logs = Directory.GetFiles(logPath + ldate, ext);
InitializeComponent();
logList = new ObservableCollection<String>();
logList.Clear();
lbLogs.ItemsSource = logList;
foreach (string logName in logs)
{
string s = logName.Substring(logName.IndexOf(ldate) + ldate.Length + 1);
int extPos = s.LastIndexOf("."); // <- finds the extension
s = s.Substring(0, extPos); // <- removes the extension
s = s.ToUpper(); // <- converts to uppercase
logList.Add(s); // <- adds the items it finds
}
DataContext = this;
}
Need to set the TimePicker value to a logTime variable then use the logTime to filter the list of items that are displayed
Successfully have used this code to get the LastWriteAccess time just need some assistance putting it all together properly
public static void Times(string sFile)
{
FileInfo info = new FileInfo(sFile);
DateTime time = info.LastWriteTime;
string s = time.ToString("HH:mm tt");
Console.WriteLine("Last Access: " + s);
}
First off, you will want to associate the time with the string representing the log file. Instead of an ObservableCollection you'll have an ObservableCollection. Log would be defined as:
public class Log
{
String LogName;
DateTime LogWriteTime;
}
Creating the collection would call your other function (modified to return the read time):
foreach (string logName in logs)
{
string s = logName.Substring(logName.IndexOf(ldate) + ldate.Length + 1);
int extPos = s.LastIndexOf("."); // <- finds the extension
s = s.Substring(0, extPos); // <- removes the extension
s = s.ToUpper(); // <- converts to uppercase
Log newItem = new Log();
newItem.LogName = s;
newItem.LogWriteTime = GetFileAccessTime(s)
logList.Add(s); // <- adds the items it finds
}
public DateTime GetFileAccessTime(string sFile)
{
FileInfo info = new FileInfo(sFile);
return info.LastWriteTime;
}
Now that we have our time stored off, I'm going to assume your TimePicker's SelectedValue property is bound to FilterTime. There are two ways to approach the problem of filtering:
Bind your view to a separate IEnumerable FilteredLogs and do the following in the setter of the FilterTime variable:
FilterdLogs = logList.Where(l => l.LogWriteTime >= FilterTime);
Use a CollectionViewSource. This method is awesome! First, create a CollectionViewSource property called LogsSource. Change your XAML to bind to this instead of the old ObservableCollection.
<ListBox ItemsSource="{Binding FilteredLogs.View}"/>
Now, in whatever you have for an Init function (constructor, whatever calls LoadLogsNoData, etc.) write:
FilteredLogs = new CollectionViewSource();
FilteredLogs.Source = logList;
FilteredLogs.Filter += CheckAccessTime;
This sets up a new CollectionViewSource that points to your logList collection, and uses the CheckAccessTime function to determine if a particular log entry should be included in the "View" property (which you previously bound to).
The CheckAccessTime function will look like:
private void CheckAccessTime(object sender, FilterEventArgs e)
{
Log logEntry = e.Item as Log;
if (logEntry != null)
{
if (logEntry.LogWriteTime >= FilterTime)
{
e.Accepted = true;
}
else
{
e.Accepted = false;
}
}
}
Finally, you need to refresh the filter anytime the selection changes. So, in the setter of FilterTime write:
FilteredLogs.View.Refresh();
The second option is, in my opinion, a much cleaner way of accomplishing the task, though it may be a bit more confusing at first. Let me me know if I can clarify anything!
This blog post was greatly helpful in researching the second method: http://uicraftsman.com/blog/2010/10/27/filtering-data-using-collectionviewsource/
MSDN for the filter event: MSDN

Lightswitch export all rows to CSV

I am using c# and VS2012 on a lightswitch web-application,
I wish to export my data to CSV (on a search screen!), but can't reach any POC,
As i understand there are 2 main problems - a savefiledialog must be caused directly from a user button and in it must happened in the main dispatcher,
I used this code :
partial void mySearchScreen_Created()
{
var CSVButton = this.FindControl("ExportToCSV");
CSVButton.ControlAvailable += ExportCSV_ControlAvailable;
}
private void ExportCSV_ControlAvailable(object sender, ControlAvailableEventArgs e)
{
this.FindControl("ExportToCSV").ControlAvailable -= ExportCSV_ControlAvailable;
Button Button = (Button)e.Control;
Button.Click += ExportCSV_Click;
}
private void ExportCSV_Click(object sender, System.Windows.RoutedEventArgs e)
{
Microsoft.LightSwitch.Details.Client.IScreenCollectionProperty collectionProperty = this.Details.Properties.mySearch;
var intPageSize = collectionProperty.PageSize;
//Get the Current PageSize and store to variable
collectionProperty.PageSize = 0;
var dialog = new SaveFileDialog();
dialog.Filter = "CSV (*.csv)|*.csv";
if (dialog.ShowDialog() == true) {
using (StreamWriter stream = new StreamWriter(dialog.OpenFile())) {
string csv = GetCSV();
stream.Write(csv);
stream.Close();
this.ShowMessageBox("Excel File Created Successfully. NOTE: When you open excel file and if you receive prompt about invalid format then just click yes to continue.", "Excel Export", MessageBoxOption.Ok);
}
}
collectionProperty.PageSize = intPageSize;
//Reset the Current PageSize
}
private string GetCSV()
{
StringBuilder csv = new StringBuilder();
int i = 0;
foreach (var orderRow_loopVariable in mySearch) {
var orderRow = orderRow_loopVariable;
////HEADER
if (i == 0) {
int c = 0;
foreach (var prop_loopVariable in orderRow.Details.Properties.All().OfType<Microsoft.LightSwitch.Details.IEntityStorageProperty>()) {
var prop = prop_loopVariable;
if (c > 0) {
csv.Append(",");//Constants.vbTab
}
c = c + 1;
csv.Append(prop.DisplayName);
}
}
csv.AppendLine("");
////DATA ROWS
int c1 = 0;
foreach (var prop_loopVariable in orderRow.Details.Properties.All().OfType<Microsoft.LightSwitch.Details.IEntityStorageProperty>()) {
var prop = prop_loopVariable;
if (c1 > 0) {
csv.Append(",");//Constants.vbTab
}
c1 = c1 + 1;
csv.Append(prop.Value);
}
i = i + 1;
}
if (csv.Length > 0) {
return csv.ToString(0, csv.Length - 1);
} else {
return "";
}
}
This works, but it only get's me the first page items,
On another thing i had to do i solved that problem by using this code :
this.DataWorkspace.myDataContextData.MySearch(...).Execute();
Yet trying that instead of just using 'MySearch' gives me the following error :
t is not valid to call Execute() on a different Dispatcher than the ExecutableObject's Logic Dispatcher.
Why is it so difficult to do such a basic thing related to data (export to csv/excel) on a system build for handling data ?
Any ideas ?
The simplest workaround if this is the only use of the search screen would be to turn off paging. To do this go to the screen designer, highlight the query on the left, and in properties uncheck 'support paging.'
I'm not sure what the limitations are, but you can run some code in a different dispatcher using:
this.Details.Dispatcher.BeginInvoke(() =>
{
//This runs on main dispatcher
});
I don't think there's anything wrong with your code, but I've noticed that it takes a while to reset the page size on a large collection, in which time the rest of your code continues to execute. I think that's why you only get the first page. The only solution I've found is to wait.
When the "File Download - Security Warning" dialog pops up, keep an eye on the 'busy' indicator on the screen's tab and also the 'Page x of y' status at the bottom of the grid if you can see it. Only when the busy indicator has gone and the status just says 'Page' should you click OK to continue.
I haven't figured out a way of doing this programmatically so it's not a very helpful feature unless you have a very tightly controlled user population. But if it's just you and a couple of power users, it is workable. I'm also not sure if this has been improved on in versions after VS2012.
There can be a downside to the other answer of taking the paging off the query entirely. I've tried that workaround when the grid collection was being displayed in a modal window and the window became uncloseable if there were too many rows in the grid.
Phil

Adding a wizard to a blank project: "The operation could not be completed. No such interface supported"

I've created a custom wizard that generates a windows form through code that lists out some SQL queries for the user. For almost all of the testing of the form and the wizard itself, adding it to a blank project would bring up the form and I could test the button click events and other general form stuff. Most recently I fleshed out the listbox's event listeners and ever since then I get this error:
Googling has lead me to a lot of posts about Ruby, which I'm not using. Other suggestions were reboot and re installation, and those proved unsuccessful. I attempted to comment out the listbox events but that did not keep the error from occuring, however, if it helps here are the events in question:
//-----------Event fired when a listbox object is double-clicked; populate the listbox with the new databases---------
public void dataList_MouseDoubleClick(object sender, EventArgs e)
{
//temp string used to hold the name of the clicked object
string selectedNAME = dataList.SelectedItem.ToString();
firstSEL.TableVar = selectedNAME;
foreach (tempDataVar t in dataVars)
{
if (t.TableVar == firstSEL.TableVar)
{
firstSEL = t;
}
}
string newQ = "SELECT COLUMN_NAME,* FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + firstSEL.TableVar + "' AND TABLE_SCHEMA= '" + firstSEL.SchemaVar + "'";//order by TABLE_NAME'";
results = GetDataSet(bldr.ToString(), newQ);
//listBox1.Items.Clear();
foreach (DataRow row in results.Tables[0].Rows)
{
//foreach (object x in row.ItemArray)
//{
// listBox1.Items.Add(x.ToString());
//}
for (int x = 0; x < row.ItemArray.Length; x++)
{
if (x == 0)
{
colList.Items.Add(row.ItemArray[x]);
}
}
}
dataList.Enabled = false;
}
//-----------------------------Event that fires when the index of the second listbox changes--------------------------
private void colList_SelectedIndexChanged(object sender, EventArgs e)
{
btnYes.Enabled = true;
}
Noobie mistake fixed by a co-worker! I blindly copied and pasted the following code from a "How to make a wizard tutorial":
[ComVisible(true)]
[Guid("20184B81-7C38-4E02-A1E3-8D564EEC2D25"),
ProgId("MyNewWizard.Class1")]
This code needed to be directly above the MyNewWizard class and I accidentally pasted in my custom TempDataVar class in the white space between these lines and the start of the MyNewWizard class. If you're receiving a similar error then I'd suggest testing around the order of some of your attributes/classes!

C# Search datagridview for duplicates

Using ms visual studio and csharp .net4.
This is the code i have to check for duplicates
public void CheckForDuplicate()
{
DataGridViewRowCollection coll = ParetoGrid.Rows;
DataGridViewRowCollection colls = ParetoGrid.Rows;
List<string> listParts = new List<string>();
int count = 0;
foreach (DataGridViewRow item in coll)//379
{
foreach (DataGridViewRow items in colls)//143641
{
if (items.Cells[5].Value == item.Cells[5].Value)
{
if (items.Cells[2].Value != item.Cells[2].Value)
{
listParts.Add(items.Cells["Keycode"].Value.ToString());
count++;
dupi = true;
//txtDupe.Text = items.Cells["Keycode"].Value.ToString();
//this.Refresh();
}
}
}
}
MyErrorGrid.DataSource = listParts;
}
This is the check before it allows the user to save.
private void butSave_Click(object sender, EventArgs e)
{
CheckForDuplicate();
if (dupi == true)
{
txtDupe.Clear();
dupi = false;
}
else
{
SaveMyWorkI();
dupi = false;
}
}
This is the data that it is looking at:
Now, I know the logic must be flawed since it saving regardless.
I'm basically searching through each cell on pareto1 to see if the user has made any duplicates, if so it will not save and instead displays the part number etc in another datagridview....well that's the plan.
So could someone look through this and tell me
1) Where in my logic is this failing, also what about if the checks are correct?
2) Will the list work adding the info, if so is a simple bind to a datagrid view enough to display the results?
3) If this is just a really bad way of searching through could someone provide code that reflects what I am Trying to achieve.
Many thanks for your future comments.
UPDATE:: Ok thanks for the help, my algorithm now works, but my very last problem is displaying the part number that is duplicated on the pareto column, instead it displays the length.
public void CheckForDuplicate()
{
DataGridViewRowCollection coll = ParetoGrid.Rows;
DataGridViewRowCollection colls = ParetoGrid.Rows;
List<string> listParts = new List<string>();
int count = 0;
foreach (DataGridViewRow item in coll)//379
{
foreach (DataGridViewRow items in colls)//143641
{
count++;
if ((items.Cells[5].Value != null))
{
if ((items.Cells[5].Value != null) && (items.Cells[5].Value.Equals(item.Cells[5].Value)))
{
if ((items.Cells[2].Value != null) && !(items.Cells[2].Value.Equals(item.Cells[2].Value)))
{
listParts.Add(items.Cells["Keycode"].Value.ToString());
dupi = true;
}
}
}
}
}
MyErrorGrid.DataSource = listParts;
var message = string.Join(Environment.NewLine, listParts);
//MyErrorGrid.DataSource = message;
MessageBox.Show(message);
}
Even though the message box correctly displays the results? is it something im missing out when binding to my datagrid?
Here is a simple example showing how to perform validation during dataentry. There are various ways you can customise how the errors appear (including some sort of custom dialog to resolve errors) that might give you a better solution.
public partial class Form1 : Form
{
BindingSource bs;
DataTable dt; public Form1()
{
InitializeComponent();
BindingList<BindingClass> data = new BindingList<BindingClass>
{
new BindingClass{ Name = "one" },
new BindingClass { Name = "two"}
};
dataGridView1.DataSource = data;
dataGridView1.CellValidating += new DataGridViewCellValidatingEventHandler(dataGridView1_CellValidating);
}
void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Index != e.RowIndex & !row.IsNewRow)
{
if (row.Cells[0].Value.ToString() == e.FormattedValue.ToString())
{
dataGridView1.Rows[e.RowIndex].ErrorText =
"Duplicate value not allowed";
e.Cancel = true;
return;
}
}
}
dataGridView1.Rows[e.RowIndex].ErrorText = string.Empty;
}
}
public class BindingClass
{
public string Name { get; set; }
}
}
Naturally this won't always fit your requirements of what users like to work with but I thought it could help to see another option.
You are doing comparisons with == and != .
items.Cells[5].Value exposes an object.
In your case this is most likely doing an equality check based on reference equality, which is probably not what your want. Try using something like items.Cells[5].Value.Equals(item.Cells[5].Value)
Please also consider solving such problems on the simplest abstractions available. E.g. if you had the grid bound to a collection of objects, then you could perform the cleanup operation against that collection of objects, disregarding any UI you bolt on top of it.
You can also consider using the Distinct extension method from the LINQ namespace and provide it an IEqualityComparer* to make sure the most efficient code for removing duplicates available in the .NET Framework is used by you.
*) IEqualityComparer is an abstraction that allows you to define in one place when you consider two objects to be equal. Distinct provides an overload where you can specify such a comparer.
See if this can work for you
var dup = dataGridView1.Rows.Cast<DataGridViewRow>().Distinct().Where(g => g.Index != 0);
Excluding row with index 0. It is header row.

Categories