Uwp Autosuggestbox OrderBy Displaymemberpath - c#

I have a behavior for my autosuggestbox where I have to order all the suggested list items by ascending order, and this behavior will is being applied to 1 common style of AutoSuggestBox across whole app. When I try ordering simply with the object itself it works fine as the items are just a list of strings. But when the items are a list of objects, and I want to sort with 1 specific property, then it is not working for me. I am using DisplayMemberPath to tell it which property it should look for. Below is the code I tried with :
Behavior
public class AutoSuggestSortBehavior : IBehavior
{
public void Attach(DependencyObject associatedObject) => ((AutoSuggestBox) associatedObject).TextChanged += AutoSuggestSortBehavior_TextChanged;
private void AutoSuggestSortBehavior_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
var autoSuggestBox = sender;
if(autoSuggestBox?.Items?.Count > 0 && args.Reason == AutoSuggestionBoxTextChangeReason.UserInput && !string.IsNullOrWhiteSpace(sender.Text))
{
if (!string.IsNullOrWhiteSpace(autoSuggestBox.DisplayMemberPath))
{
autoSuggestBox.ItemsSource = autoSuggestBox.Items.ToList().OrderBy(x => x.GetType().GetProperty(autoSuggestBox.DisplayMemberPath).Name).ToList();
}
else
{
autoSuggestBox.ItemsSource = autoSuggestBox.Items.ToList().OrderBy(x => x).ToList();
}
}
}
public void Detach(DependencyObject associatedObject) => ((AutoSuggestBox) associatedObject).TextChanged -= AutoSuggestSortBehavior_TextChanged;
}
Xaml
<AutoSuggestBox
Header="AutoSuggest"
QueryIcon="Find"
Text="With text, header and icon"
TextChanged="AutoSuggestBox_TextChanged" />
<AutoSuggestBox
DisplayMemberPath="Name"
Header="AutoSuggest2"
QueryIcon="Find"
Text="With text, header and icon"
TextChanged="AutoSuggestBox_TextChanged2" />
TextChanged events
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
var abcc = new List<string>();
abcc.Add("xyz");
abcc.Add("321");
abcc.Add("123");
abcc.Add("lopmjk");
abcc.Add("acb");
sender.ItemsSource = abcc;
}
private void AutoSuggestBox_TextChanged2(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
var persons = new List<Person>();
persons.Add(new Person { Name = "xyz", count = 1 });
persons.Add(new Person { Name = "321", count = 2 });
persons.Add(new Person { Name = "123", count = 3 });
persons.Add(new Person { Name = "lopmjk", count = 4 });
persons.Add(new Person { Name = "acb", count = 5 });
sender.ItemsSource = persons;
}

So I found the solution with a little experiment, we can use the GetValue() method with passing in the object itself and then it works as expected :
autoSuggestBox.ItemsSource = autoSuggestBox.Items.ToList().OrderBy(x => x.GetType().GetProperty(autoSuggestBox.DisplayMemberPath).GetValue(x)).ToList();

Related

Merge an array with a ListView

I've been trying to finish a project of mine studying C# for now and have been encountered with a problem.
The task is to create ill or healthy animals and separate them to different arrays.
Everything worked fine until I tried to merge between an array to a ListView.
What I want to do is to use the array's information and to insert it into the list's columns, which I don't know how to do.
It's purpose is to make each array communicate with the list, when you add or remove animals from any array, it deletes the specific animal from the list, and same thing about adding animals.
This is what I've been trying to do but it is not a good job. I need to collect the info from the array and not from the textboxes.
private void btnAddStack_Click(object sender, EventArgs e)
{
if (txtCode.Text == "Example: 1234")
{
MessageBox.Show("Fill in animal's code in order to proceed", "Error");
txtCode.Focus();
return;
}
if (txtName.Text == "Example: Lion")
{
MessageBox.Show("Fill in animal's name in order to proceed", "Error");
txtName.Focus();
return;
}
if (txtWeight.Text == "Example: 100")
{
MessageBox.Show("Fill in animal's weight in order to proceed", "Error");
txtWeight.Focus();
return;
}
if (rbFemale.Checked == false && rbMale.Checked == false)
{
MessageBox.Show("Fill in animal's gender in order to proceed", "Error");
return;
}
if (rbNoMarine.Checked == false && rbYesMarine.Checked == false)
{
MessageBox.Show("Choose if the animal is marine or not in order to proceed", "Error");
return;
}
btnTakeCareSt.Enabled = true;
service.addNewAnimal(int.Parse(txtCode.Text), txtName.Text, float.Parse(txtWeight.Text), CheckMarine(), CheckUrgent(), CheckGender());
lvStack.Items.Add(new ListViewItem(new[] {txtName.Text, txtCode.Text, txtWeight.Text, BoolToString(), CheckMarine().ToString()}));
}
The size of the Array is fixed, you cannot add or remove its items dynamically. Maybe using List is a better choice.
Here I have a class Student for test, and you can define a List to store its instances.
/// <summary>
/// Student class for test
/// </summary>
class Student
{
public string Name { get; set; }
public int Age { get; set; }
}
Define a list in Form class:
// Define a list to store students
List<Student> students = new List<Student>() { new Student { Name = "A", Age = 10 },
new Student { Name = "B", Age = 9 },
new Student { Name = "C", Age = 14 } };
Then you can refer to the following code to store the items in list to ListView.
private void Form1_Load(object sender, EventArgs e)
{
listView1.View = View.Details;
this.listView1.Columns.Add("Name", 120, HorizontalAlignment.Left);
this.listView1.Columns.Add("Age", 120, HorizontalAlignment.Left);
this.listView1.BeginUpdate();
for (int i = 0; i < students.Count; i++)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = students[i].Name;
lvi.SubItems.Add(students[i].Age.ToString());
this.listView1.Items.Add(lvi);
}
this.listView1.EndUpdate();
}
To add a new item, please refer to:
private void btnAdd_Click(object sender, EventArgs e)
{
// Add a new student named "D" and age is 17;
string name = "D";
int age = 17;
// Add new student to list and ListView
Student student = new Student() { Name = name, Age = age };
students.Add(student);
ListViewItem lvi = new ListViewItem();
lvi.Text = student.Name;
lvi.SubItems.Add(student.Age.ToString());
this.listView1.Items.Add(lvi);
}
As to remove a specific item, you can use:
private void btnRemove_Click(object sender, EventArgs e)
{
// Remove the student whose name is "B"
string name = "B";
// Remove the data in list
var found = students.Find(x => x.Name == name);
if (found != null) students.Remove(found);
// Remove the item in ListView
ListViewItem foundItem = this.listView1.FindItemWithText(name, true, 0);
if (foundItem != null)
{
listView1.Items.Remove(foundItem);
}
}

New list updates original one

Here i am using this code for copy one list to another
public class Person
{
public string Name { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
var originalList = new List<Person>();
originalList.Add(new Person { Name = "name 1" });
originalList.Add(new Person { Name = "name 2" });
// var newList = originalList.ToList();
var newList = new List<Person>(originalList);
newList[0].Name = "New name";
Console.WriteLine(originalList[0].Name);
}
My result in console is 'New name', why this is happen? When i am updating my new list it also updates my original one. How can i fix this?
Do not worry, this is normal behavior, you have the original list, then you have another list filled in by the original, in your case, both lists point to the same items, which means that you have two references that point to the same memeory case , reason why you change an element from the original one the same element change in the second one and vice-versa .
Case 1 :
public class Person
{
public string Name { get; set; }
}
private void Form1_Load(object sender, EventArgs e)
{
//here you have the original list, you create your list
//you add element to your list .
var originalList = new List<Person>();
originalList.Add(new Person { Name = "name 1" });
originalList.Add(new Person { Name = "name 2" });
// you create a second list , but here the contain the
// same element than the original list
var newList = new List<Person>(originalList);
newList[0].Name = "New name";
Console.WriteLine(originalList[0].Name);
}

Get selected value from asp SharePoint DropDownList of custom class type

I am writing a SharePoint app. There I have page with drop down list. I have
a handler for SelectedIndexChanged. I want to get the selected value but as CustomObject and the only option I see is string. I tried SelectedValue and it is still string.
That's how I set the list:
protected void Page_Load(object sender, EventArgs e)
{
List<CustomObject> customList = //retrieving data
myDropDownList.DataSource = customList.Select(x => new { x.Name, Value = x});
myDropDownList.DataTextField = "Name";
myDropDownList.DataValueField = "Value";
myDropDownList.DataBind();
}
And that's one of the ways I tried:
protected void myDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
var index = groupingDropDownList.SelectedIndex;
CustomObject obj = (CustomObject)myDropDownList.Items[index].Value;
obj.DoSomething();
}
Is it even possible? Or do I have to have somewhere Dictionary with with objects?
You will want to leverage the html5 data attributes that you can then place onto the dropdown options. Here is an example of what you could do with your data.
// add Agencies' addresses as HTML5 data-attributes.
var agencies = agencyNames.ToList();
for (int i = 0; i < requesting_agency.Items.Count - 1; i++) {
requesting_agency.Items[i + 1].Attributes.Add("data-address",
agencies[i].address);
servicing_agency.Items[i + 1].Attributes.Add("data-address",
agencies[i].address);
}
Then when processing the information you could do something like so.
var index = groupingDropDownList.SelectedIndex;
var selectedText = myDropDownList.Items[index].SelectedValue;
var selectedValue = myDropDownList.Items[index].Attributes["attribute"];
// put into obj
// do something with object
Let me know if you have any questions.
You 're binding a object (x => new { x.Name, Value = x}) to dropdown value, you should bind actual value to it.
Test demo:
public class CustomObject
{
public int ID { get; set; }
public string Name { get; set; }
public CustomObject(int _ID,string _Name)
{
this.ID = _ID;
this.Name = _Name;
}
}
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
List<CustomObject> customList = new List<CustomObject>();
customList.Add(new CustomObject(1,"test1"));
customList.Add(new CustomObject(2,"test2"));
myDropDownList.DataSource = customList.Select(x => new { x.Name, Value = x.ID });
myDropDownList.DataTextField = "Name";
myDropDownList.DataValueField = "Value";
myDropDownList.DataBind();
}
}

wondering if there is an efficient way to shorten this code

So my program is going to end up being fairly large and I don't want a whole lot of code that could just be shortened. Here is one instance I am looking for some tips on:
private void bookComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
string books = null;
// sets books to the clicked item
books = bookComboBox.SelectedItem.ToString();
selectedPictureBox.Visible = true;
// Loads string to list box and image to selectedPictureBox when programming is selected
if (books == "Programming")
{
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile("programming.png");
bookListBox.Items.Add("Visual Basic");
bookListBox.Items.Add("Java");
bookListBox.Items.Add("C#");
}
// Loads string to list box and image to selectedPictureBox when Networking is selected
else if (books == "Networking")
{
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile("networking.png");
bookListBox.Items.Add("LAN Networks");
bookListBox.Items.Add("Windows Networking");
bookListBox.Items.Add("More About Networking");
}
// Loads string to list box and image to selectedPictureBox when Web is selected
else if (books == "Web")
{
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile("html.png");
bookListBox.Items.Add("Web Programming");
bookListBox.Items.Add("JavaScript");
bookListBox.Items.Add("ASP");
}
}
The code works fine but I was just hoping to get some tips on shortening this code if necessary, any input is appreciated.
Assuming you can use C# 7's new Tuples:
private Dictionary<string, (string image, List<string> books)> books = new Dictionary<string, (string image, List<string> books)>
{
{ "Programming", ("programming.png", new List<string> { "Visual Basic", "Java", "C#"} ) },
{ "Networking", ("networking.png", new List<string> {"LAN Networks", "Windows Networking", "More About Networking"}) },
{ "Web", ("html.png", new List<string> {"Web Programming", "Javascript", "ASP"}) }
};
private void bookComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
// sets books to the clicked item
string book = bookComboBox.SelectedItem.ToString();
selectedPictureBox.Visible = true;
if (books.Keys.Contains(book))
{
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile(books[book].image);
foreach(var b in books[book].books)
{
bookListBox.Items.Add(b);
}
}
}
But a class is likely even better:
public class BookGroup
{
public string ImagePath {get;set;}
public List<string> Books {get;}
public BookGroup(string imagePath, params string[] books)
{
ImagePath = imagePath;
Books = new List<string>(books.Length);
Books.AddRange(books);
}
}
Which isn't all that different to use right now, but it formalizes some things that might make this code easier to work with down the road, and it's possible if you can't use Tuples yet. I might also have a Book class by itself, just for fun, but for now:
private Dictionary<string, BookGroup> books = new Dictionary<string, BookGroup>
{
{ "Programming", new BookGroup("programming.png", "Visual Basic", "Java", "C#")},
{ "Networking", new BookGroup("networking.png","LAN Networks", "Windows Networking", "More About Networking") },
{ "Web", new BookGroup("html.png", "Web Programming", "Javascript", "ASP") }
};
private void bookComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
// sets books to the clicked item
string book = bookComboBox.SelectedItem.ToString();
selectedPictureBox.Visible = true;
if (books.Keys.Contains(book))
{
bookListBox.Items.Clear();
BookGroup bg = books[book];
selectedPictureBox.Image = Image.FromFile(bg.ImagePath);
foreach(var b in bg.Books)
{
bookListBox.Items.Add(b);
}
}
}
Regardless, I'd definitely have a way to load these from a text file... likely a csv, or maybe even a small in-process database, so that I could update this listing without having to recompile or distribute new program code. And, with that in mind, in order to fit this data in a single structure in a single file, I'd likely also repeat the image and type with each book, so that my csv data looks like this:
Topic,Image,Title
Programming,programming.png,"Visual Basic"
Programming,programming.png,"Java"
Programming,programming.png,"C#"
Networking,networking.png,"LAN Networks"
Networking,networking.png,"Windows Networking"
Networking,networking.png,"More About Networking"
Web,html.png,"Web Programming"
Web,html.png,"Javascript"
Web,html.png,"ASP"
That changes the whole character of the code. I'm a bit biased, but I'd likely use this CSV Parser, and again assuming Tuples I'd produce something like this:
private List<(string Topic, string ImagePath, string Title)> books;
//In the form load code:
books = EasyCSV.FromFile("bookData.csv").Select(b => (b[0], b[1], b[2])).ToList();
//and finally, in the original selectindexchanged method:
private void bookComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
string topic = bookComboBox.SelectedItem.ToString();
selectedPictureBox.Visible = true;
var items = books.Where(b => b.Topic == topic).ToArray();
if(items.Length > 0)
{
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile(items[0].ImagePath);
bookListBox.Items.AddRange(items);
}
}
Make objects and use databindings.
public class Book
{
public BookType BookType { get; set; }
public string Name { get; set; }
public string Image { get; set; }
}
public enum BookType
{
Programming,
Networking,
Web,
}
public partial class Form1 : Form
{
private readonly List<Book> _books = new List<Book>
{
new Book { Image = "programming.png", BookType = BookType.Programming, Name = "VB" },
new Book { Image = "programming.png", BookType = BookType.Programming, Name = "Java" },
new Book { Image = "programming.png", BookType = BookType.Programming, Name = "C#" },
new Book { Image = "networking.png", BookType = BookType.Networking, Name = "LAN Networks" },
new Book { Image = "networking.png", BookType = BookType.Networking, Name = "Windows Networking" },
new Book { Image = "networking.png", BookType = BookType.Networking, Name = "More About Networking" },
new Book { Image = "html.png", BookType = BookType.Web, Name = "Web Programming" },
new Book { Image = "html.png", BookType = BookType.Web, Name = "Javascript" },
new Book { Image = "html.png", BookType = BookType.Web, Name = "ASP" },
};
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var bookTypes = _books.GroupBy(b => b.BookType).Select(g => g.Key).ToList();
this.cboBookTypes.DataSource = bookTypes;
}
private void cboBookTypes_SelectedIndexChanged(object sender, EventArgs e)
{
var bookType = (BookType)this.cboBookTypes.SelectedItem;
var books = _books.Where(b => b.BookType == bookType).ToList();
var img = books.First().Image;
this.imgBook.Image = Image.FromFile(img);
this.lstBooks.DataSource = books;
this.lstBooks.DisplayMember = "Name";
}
}
If you are talking about the length of the code, I would suggest using switch-case-break-default construct
Switch the books variable.
This wont improve the performance though
I think you should create a class that represents book category. Then, you could simply iterate through all the category lists and extract the necessary information, like this:
string books = null;
books = bookComboBox.SelectedItem.ToString();
selectedPictureBox.Visible = true;
for (int i = 0; i < categories.Count; i++) {
if (books == categories[i].Name) {
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile(categories[i].ImagePath);
for (int j = 0; j < categories[i].Items.Count; j++) {
bookListBox.Items.Add(categories[i].Items[j]);
}
}
}
I would suggest to keep all the data in a configuration object and then iterate through that data when performing checks and assignments.
I would create a separate class to hold data for each book: name, picture file name and check box items string array.
Then I would create a list of that object and assign all the data manually on form initialization.
After that, in SelectedIndexChanged event handler, I would iterate (for loop) on each item and check if the book name matched. If it did, then I would use data from that object and then break; the loop.
I do not have visual studio, so giving you the points/suggestions to improve on.
switch should be preferred over if-elseif.
bookListBox.Items.Clear(); and selectedPictureBox.Image out of if block. Use a variable to set the image file name.
Create a class to represent a book list:
public class BookList
{
public string ImageName { get; set; }
public List<string> Items { get;set; }
}
Then create a dictionary to hold these items:
Dictionary<string, BookList> bookLists = new Dictionary<string, BookList>
{
{
"Programming",
new BookList { ImageName = "programming.png", Items = new List<string> { ... } }
}
...
};
Then modify your if statements to:
if (bookLists.ContainsKey(books))
{
bookListBox.Items.Clear();
selectedPictureBox.Image = Image.FromFile(bookLists[books].ImageName);
foreach (var b in bookLists[books].Items)
{
bookListBox.Items.Add(b);
}
}

c# windows 8 selected comboboxitem

I'm developping a Windows 8 Store Application (c#).
I have a Combobox (cboTeam1) that gets the items from a repository.
private static List<TeamItem> JPLItems = new List<TeamItem>();
public static List<TeamItem> getJPLItems()
{
if (JPLItems.Count == 0)
{
JPLItems.Add(new TeamItem() { Id = 1, Description = "Anderlecht", Image = "Jpl/Anderlecht.png", ItemType = ItemType.JPL });
JPLItems.Add(new TeamItem() { Id = 1, Description = "Beerschot", Image = "Jpl/Beerschot.png", ItemType = ItemType.JPL });
JPLItems.Add(new TeamItem() { Id = 1, Description = "Cercle Brugge", Image = "Jpl/Cercle.png", ItemType = ItemType.JPL });
JPLItems.Add(new TeamItem() { Id = 1, Description = "Charleroi", Image = "Jpl/Charleroi.png", ItemType = ItemType.JPL });
}
return JPLItems;
}
I load the items in the cboTeam1's ItemsSource:
cboTeam1.ItemsSource = ItemRepository.getJPLItems();
When cboTeam1 selectionchanged I do this:
private void cboTeam1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Ploeg1.Text = cboTeam1.SelectedValue.ToString();
}
This results in: SportsBetting.Model.TeamItem
Can anyone help me to get the combobox selectedvalue in my textblock (Ploeg1.Text)??
You've nearly answered this for yourself.
private void cboTeam1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// cast the selected item to the correct type.
var selected = cboTeam.SelectedValue as TeamItem;
//then access the appropriate property on the object, in this case "Description"
// note that checking for null would be a good idea, too.
Ploeg1.Text = selected.Description;
}
The other option would be to override ToString() in your TeamItem class to return Description. In that case your original code should work fine.
public override string ToString()
{
return this._description; // assumes you have a backing store of this name
}

Categories