how to avoid the repeated code to increase the efficiency - c#

I have a DataGrid view1 and a ListView and when ever I select the list view item(I am passing the ListView item into the query and populating the DataGrid view according that item)
I have wrote some code like this....
private void listview_selectedindexchanged(object sender event args)
{
if (listview.SelectedItems.Count > 0 && listview.SelectedItems[0].Group.Name == "abc")
{
if(lstview.SelectedItems[0].Text.ToString() == "sfs")
{
method1();
}
else
{
// datagrid view1 binding
blah.....
}
}
if (lstview.SelectedItems.Count > 0 && lstview.SelectedItems[0].Group.Name == "def")
{
if(lstview.SelectedItems[0].Text.ToString() == "xyz")
{
method 1();
}
if(lstview.SelectedItems[0].Text.ToString() == "ghi")
{
method 2(a,b);
}
if(lstview.SelectedItems[0].Text.ToString() == "jkl")
{
method 2(c,d);
}
if(lstview.SelectedItems[0].Text.ToString() == "mno")
{
method 3();
}
}
}
private void method 1()
{
// datagrid view1 binding
blahh
}
private void method 2(e,g)
{
// datagrid view1 binding
blah....blah..
}
private void method 3()
{
// datagrid view1 binding
}
I have done it like above ... I think this is not an efficient way to do the coding. and this code consisits of a lot of repeated lines, is there any way to refractor this code to a small bunch of code ......
in order improve the efficiency?
Any ideas and sample snippets for increasing code efficiency would be helpful to me ...
Many thanks in advance....
I am using c# and writting WinForms applications.....

You could save a delegate into the listview item. And call it when the encapsulating item gets selected. For example you would fill your listbox like this:
ListViewItem item = new ListViewItem("abc");
item.Tag = new Delegate(method1);
lstview.Items.Add(item);
Now, when this item gets selected, you execute the method like so:
private void listview_selectedindexchanged(object sender event args)
{
((Delegate)lstview.SelectedItems[0].Tag)(); // this will execute method1 if the item with text "abc" gets selected
}
NOTE: ! have not tested this code, but something along those lines should work and you don't have to write the If-statement, you only have to construct the items correctly.
Also note that this may be a bit hard to read for someone new to this code.

You could easily extract a new method to do the "datagrid view1 binding". This method is then called from all the methods that need to do the binding.

Related

Retrieve column of Listview items and display in a comboBox on another form c#

I have 2 forms: 1 form contains a listView and another form contains a comboBox.
I would like the first column of the listView to be loaded into the comboBox on the second form.
This is my attempt:
comboBox1.Items.Add(Form2.listView2.columnHeader1);
However, this does not work. (Form2.ListView is inaccessible due to its protection level). Suggestions would be appreciated.
Quick and dirty solution:
Go to the Properties of listView2 in the winforms designer and look for Modifiers. Then select Public like you see in the picture below:
And now it will be accessible
The more elegant solution:
Create a property in your first Form which has only a getter. In this getter you can safely return the columnHeader1:
public ColumnHeader ColumnHeader { get { return this.listView1.Columns["columnHeader1"]; }}
or:
public ColumnHeader ColumnHeader { get { return this.columnHeader1; }}
EDIT:
It seems that you rather would like to have all values from that column. So in this case you would have to return all the values, which can be done like this:
public List<string> AlllValuesFromColumn
{
get
{
int indexOfColumn = listView2.Columns.IndexOf(this.columnHeader1);
return listView2.Items.OfType<ListViewItem>().Select(x => x.SubItems[indexOfColumn].Text).ToList();
}
}
To Add all the values in one blow to the ComboBox you can use AddRange:
comboBox1.Items.AddRange(Form2.AlllValuesFromColumn.ToArray());
EDIT 2:
But the solution I personally would prefer is to hold the data source in an extra variable. This can be passed around. No magic there.
Make a public method in your second form and let the method set the comboboxitems.
Form1:
bool Do = true;
int i = 0;
Form2 F = new Form2();
while (Do)
{
try
{
F.AddItem(listView1.Columns[i].Name);
i++;
}
catch
{
Do = false;
}
}
Form2:
public void AddItem(string ToAdd)
{
comboBox1.Items.Add(ToAdd);
}

How can I bind a control to a List<string>?

Seems like a very basic MVVM question, but when it comes to Catel, I have issues doing it.
I have a property - registered as it should - which is a List, named Lines.
I bind it to a ListBox.
I also have a Button with a command adding a entry to Lines.
Lines is mapped to a model, and when I check the values of the model, I see it gets updated correctly when adding a value to Lines.
So everything seems to work, except that my view isn't updating when Lines is modified.
I tried to solve this by adding a RaisePropertyChanged("Lines") in Lines' setter, and in the command that adds a new value to Lines.
It gives something like this for the property:
[ViewModelToModel("MyModel", "Lines")]
public List<string> Lines
{
get { return GetValue<List<string>>(LinesProperty); }
set
{
SetValue(LinesProperty, value);
RaisePropertyChanged("Lines");
}
}
public static readonly PropertyData LinesProperty =
RegisterProperty("Lines", typeof(List<string>), null, (s, e) => {});
and this for the command (yes, I have AddLine = new Command(OnAddLineExecute); in the viewmodel's constructor):
public Command AddLine { get; private set; }
private async void OnAddLineExecute()
{
// this doesn't seem relevant, but since we're talking async and stuff, that may as well be the issue
if (!lineCountChecked && Lines.Count >= 4)
{
if (await messageService.Show(MainDialogs.LineCountCheck, "Lines count", MessageButton.OKCancel, MessageImage.Warning) != MessageResult.OK)
return;
else
lineCountChecked = true;
}
//
Lines.Add("New Line");
RaisePropertyChanged("Lines");
}
It's more than likely a very stupid mistake, but I can't get it. What did I miss? Thanks
You have 2 options to make this work:
1) RaisePropertyChanged(() => Lines) => will update the whole collection
2) Use ObservableCollection instead of List so the UI can actually respond to updates
I recommend 2.

C# ListView from Another Function

Please forgive me for such a stupid question. I am sure many of you will find this easy, where I have sent almost half the day reading trying to figure this out.
Here is the problem:
I have a FORM (Form1.cs) made. In that form I created a listview, and named it "ListView1".
Within the Form1.cs, I call a function called FileManager(this), where I pass in the THIS object.
In FileManager.cs I was able to listviewArray= originalForm.Controls.Find("listView1", true) and find that 'listview'.
When I do a listviewArray[0]<-- I can't seem to add a list to it.
FileManager.cs
FileManager(object sender)
{
if (sender != null)
{
originalForm = (Form)sender;
}
}
public void getFiles()
{
filePaths = Directory.GetFiles(hsocDir);
if(filePaths != null)
{
listviewArray= originalForm.Controls.Find("listView1", true);
if(listviewArray != null)
{
ListViewItem lvi = new ListViewItem("text");
// My Array is listViewArray
// How to add things to Lvi to it.
}
}
== Form1.cs
public Form1()
{
InitializeComponent(`enter code here`);
mysql = new MySQLCheck(this);
fileManager = new FileManager(this);
fileManager.getFiles();
}
You can't access element 0 of the collection because the collection is empty. To add an item, use:
listViewArray.Items.Add(lvi);
You need to modify the Items collection instead of the ListView itself for this to work, as ListView is not a collection (its a control).
listViewArray.Items.Add(lvi);
Also in your listview,setting this properties will help :
// Set the view to show details.
listViewArray.View = View.Details;
// Select the item and subitems when selection is made.
listViewArray.FullRowSelect = true;
// Display grid lines.
listViewArray.GridLines = true;

The BindingList Datasource of a Combobox refreshes correctly but the Combobox displays items in the wrong order

I have a BindingList< KeyValuePair < string, string > > that is bound to a ComboBox control. Based on some conditions, the BindingList will be added a new KeyValuePair. Now, the Newly added item shows up at index 0 of the Combobox, instead of at the end.
While debugging, I found that the BindingList has got the right order. (i.e, the new KeyValuePair is appended)
Also, I check the SelectedValue of the ComboBox in it's SelectedIndexChanged handler and it seems to be not of the ListItem that got selected. Instead, it is that of the supposed ListItem, if the ComboBox had got the right order as in its DataSource, - the BindingList..
The code is a small part of a large project.. Plz let me know if the question is not clear. I can put the relevant parts of the code as per our context.
How could something like this happen? What can I do differently?
I have this class something like this.
public class DropdownEntity
{
//removed all except one members and properties
private string frontEndName
public string FrontEndName
{
get {return this.frontEndName; }
set {this.frontEndName= value; }
}
//One Constructor
public DropdownEntity(string _frontEndName)
{
this.FrontEndName = _frontEndName;
//Removed code which initializes several members...
}
//All methods removed..
public override string ToString()
{
return frontEndName;
}
}
In my windows form, I have a tab control with several tabs. In one of the tabs pages, I have a DataGridView. The user is supposed to edit the cells and click on a Next - button. Then, some processing will be done, and the TabControl will be navigated to the next tab page.
The next tab page has the combobox that has the problem I mentioned. This page also has a back button, which will take back.. the user can modify the gridview cells again.. and click on the next button. This is when the order gets messed up.
I am posting here the Click event handler of the Next Button.. Along with the class, with the rest of the code removed.
public partial class AddUpdateWizard : Form
{
//Removed all members..
BindingList<KeyValuePair<string, string>> DropdownsCollection;
Dictionary<string, DropdownEntity> DropdownsDict;
//Defined in a partial definition of the class..
DataGridView SPInsertGridView = new DataGridView();
ComboBox DropdownsCmbBox = new ComboBox();
Button NextBtn2 = new Button();
Button BackBtn3 = new Button();
//Of course these controls are added to one of the panels
public AddUpdateWizard(MainForm mainForm)
{
InitializeComponent();
DropdownsDict = new Dictionary<string, DropdownEntity>();
}
private void NextBtn2_Click(object sender, EventArgs e)
{
string sqlArgName;
string frontEndName;
string fieldType;
for (int i = 0; i < SPInsertGridView.Rows.Count; i++)
{
sqlArgName = "";
frontEndName = "";
fieldType = "";
sqlArgName = SPInsertGridView.Rows[i].Cells["InsertArgName"].Value.ToString().Trim();
if (SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value != null)
{
frontEndName = SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value.ToString().Trim();
}
if (SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value != null)
{
fieldType = SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value.ToString().Trim();
}
//I could have used an enum here, but this is better.. for many reasons.
if (fieldType == "DROPDOWN")
{
if (!DropdownsDict.ContainsKey(sqlArgName))
DropdownsDict.Add(sqlArgName, new DropdownEntity(frontEndName));
else
DropdownsDict[sqlArgName].FrontEndName = frontEndName;
}
else
{
if (fieldType == "NONE")
nonFieldCount++;
if (DropdownsDict.ContainsKey(sqlArgName))
{
DropdownsDict.Remove(sqlArgName);
}
}
}
//DropdownsCollection is a BindingList<KeyValuePair<string, string>>.
//key in the BindingList KeyValuePair will be that of the dictionary.
//The value will be from the ToString() function of the object in the Dictionary.
DropdownsCollection = new BindingList<KeyValuePair<string,string>>(DropdownsDict.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.ToString())).ToList());
DropdownsCmbBox.DataSource = DropdownsCollection;
DropdownsCmbBox.DisplayMember = "Value";
DropdownsCmbBox.ValueMember = "Key";
//Go to the next tab
hiddenVirtualTabs1.SelectedIndex++;
}
private void BackBtn3_Click(object sender, EventArgs e)
{
hiddenVirtualTabs1.SelectedIndex--;
}
//On Selected Index Changed of the mentioned Combobox..
private void DropdownsCmbBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (DropdownsCmbBox.SelectedValue != null)
{
if (DropdownsDict.ContainsKey((DropdownsCmbBox.SelectedValue.ToString())))
{
var dropdownEntity = DropdownsDict[DropdownsCmbBox.SelectedValue.ToString()];
DropdownEntityGB.Text = "Populate Dropdowns - " + dropdownEntity.ToString();
//Rest of the code here..
//I see that the Datasource of this ComboBox has got the items in the right order.
// The Combobox's SelectedValue is not that of the selected item. Very Strange behavior!!
}
}
}
}
The very first time the user clicks the Next Button, it's fine. But if he clicks the Back Button again and changes the Data Grid View cells.. The order will be gone.
I know, it can be frustrating to look at. It's a huge thing to ask for help. Any help would be greatly appreciated!
Please let me know if you need elaboration at any part.
Thanks a lot :)
I think you have two problems here.
First, if you want to retain the order of the items you should use an OrderedDictionary instead of a regular one. A normal collection will not retain the order of the items when you use Remove method. You can see more info about this related to List here.
You could use such dictionary like this:
DropDownDict = new OrderedDictionary();
// Add method will work as expected (as you have it now)
// Below you have to cast it before using Select
DropDownCollection = new BindingList<KeyValuePair<string, string>>(DropDownDict.Cast<DictionaryEntry>().Select(kvp => new KeyValuePair<string, string>(kvp.Key.ToString(), kvp.Value.ToString())).ToList());
The second problem could be that you change the display name (FrontEndName) of already existing items, but the key is preserved. When you add a new item, try to remove the old one that you're not using anymore and add a new item.
The Sorted Property of the Combobox is set to True! I didn't check that until now. I messed up. Terribly sorry for wasting your time Adrian. Thanks a lot for putting up with my mess here.. :)

Filtering CollectionViewSource

I want to make a ComboBox bound to my data, with a filter. For that I've created a TextBox and a ComboBox. In the code behind I read a file and generate objects of class Channel that are stored as items of the ComboBox. Although the compiler throws no error the filtering doesn't work properly. If I write something the data is gone, if I erase, it's back. After trying and trying I've realized that if I started typing "myNamespace.myChannel" (Unico.Canal) the data remained, but don't filter. Strange behaviour, indeed. I suspect that I've put something in wrong place.
(for better understanding I've translated the code, Canal=Channel)
Here is the scheme of my code:
namespace Unico
{
public partial class ControlesArchivo : UserControl, INotifyPropertyChanged
{
public ControlesArchivo()
{
InitializeComponent();
}
public ObservableCollection<Channel> myListChannels //with INotifyPropertyChanged implemented. But I think I don't need it.
private void loadButton_Click(object sender, RoutedEventArgs e)
{
File loadedFile = new File();
loadedFile.read(); //Generates a bunch of data in lists.
foreach (Channel mychan in loadedFile.channels) //Just duplicating the data (maybe this can be avoided)
{
myListChannels.Add(mychan);
}
var view = CollectionViewSource.GetDefaultView(this.miListaDeCanales);
view.Filter = delegate(object o)
{
if (o.ToString().Contains(myTextBox.Text)) //Delicate place
{
return true;
}
return false;
};
myComboBox.ItemsSource = view;
DataContext = this;
}
private void myTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((ICollectionView)myComboBox.ItemsSource).Refresh();
myComboBox.SelectedIndex = 0;
}
}
}
The data is bound in XAML with:
ItemsSource="{Binding view}"
EDIT: I think I know where is the problem: I'm not specifing the property to filter. I mean, what you see in the ComboBox is the property channelName of the class Channel listed in myListChannels. When I'm setting the filter, shouldn't I let know what I'm filtering? How could I write this? Thank you very much.
Yes your assumption is correct.
I'm assuming with your translations,
public ObservableCollection<Channel> myListChannels;
is actually
public ObservableCollection<Canal> miListaDeCanales;
with the class Canal in the namespace Unico
Update:
In your filter try using the property that is rendered in the ComboBox than use the ToString() on the object(o) if you've not overridden ToString() from System.Object.
try switching
if (o.ToString().Contains(myTextBox.Text))
to
if (((Canal)o).NameProperty.Contains(myTextBox.Text))
^^ that should fix your issue.
Do you have a DataTemplate for ComboBox.ItemTemplate in xaml. That will explain why you see the valid value rendered in the ComboBox, else all the ComboBoxItem's will also render as Unico.Canal

Categories

Resources