I have an ObservableCollection called States
in the States Model I have id, name, code, country
I would like to keep this collection with all of them but I would like to make a new collection to filter only the country. Currently I have this working by using this:
ViewModel.cs
StateCollectionView = CollectionViewSource.GetDefaultView(States);
StateCollectionView.Filter += StatesFilter;
public static void GetStates()
{
States.Clear();
using var conn = new SqlConnection(Settings.Default.ConnectionString);
conn.Open();
string qry = "SELECT * FROM dbo.State";
var cmd = new SqlCommand(qry, conn);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
States.Add(new States(reader));
}
conn.Close();
}
public static bool StatesFilter(object state)
{
bool result = true;
if (state is States states)
{
if (states.Country)
{
Debug.Print(states.Name);
return true;
}
else
{
return false;
}
}
return result;
}
This is working by filtering the ObservableCollection of States but I would not like to filter that collection but rather create a ICollectionViewSource and use that in the binding in the view. As I need the States ObservableCollection to not be filtered to display correct data in other views.
I am trying to bind it to a ComboBox on the country drop down I want it to only show counties and the states dropdown to only show states. They are set apart by a bool value in the database.
EDIT: Sample
StateCollectionView = CollectionViewSource.GetDefaultView(States);
CountryCollectionView = CollectionViewSource.GetDefaultView(States);
StateCollectionView.Filter += StatesFilter;
CountryCollectionView.Filter += CountryFilter;
public static void GetStates()
{
States.Clear();
using var conn = new SqlConnection(Settings.Default.ConnectionString);
conn.Open();
string qry = "SELECT * FROM dbo.State";
var cmd = new SqlCommand(qry, conn);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
States.Add(new States(reader));
}
conn.Close();
}
private static bool StatesFilter(object state) => state is States states && !states.Country;
private static bool CountryFilter(object state) => state is States states && states.Country;
public static ObservableCollection<States> States { get; set; } = new ObservableCollection<States>();
public static ICollectionView StateCollectionView { get; set; }
public static ICollectionView CountryCollectionView { get; set; }
You are not actually filtering the ObservableCollection<T> source collection but a view of it. But the thing is that WPF are always binding to this view instead of the actual source collection.
So instead of binding one control directly to the ObservableCollection<T> and another one to the filtered ICollectionView, you should create another (unfiltered) ICollectionView and bind to this one instead of the source collection. The other option would be to create two separate source collections and filter only one of them.
If you go with two ICollectionViews, you need to filter them separately using two different methods:
private static bool StatesFilter(object state) => state is States states && !states.Country;
private static bool CountryFilter(object state) => state is States states && states.Country;
Note that you need to create the views using the constructor instead of calling CollectionViewSource.GetDefaultView:
StateCollectionView = new ListCollectionView(States);
CountryCollectionView = new ListCollectionView(States);
Related
Having a convertion issue between my WCF service returned ObservableCollection and handling the result to fill a ListView.
So in both WCF and PCL Projects i have this Temoignage.cs in Model folder
public class Temoignage
{
public string Nom { get; set; }
public string Prenom { get; set; }
public int Note { get; set; }
public string Texte { get; set; }
}
The WCF service returning this
public ObservableCollection<Temoignage> GetTemoignage()
{
ObservableCollection<Temoignage> TemoignageList = new ObservableCollection<Temoignage>();
con.Open();
SqlCommand cmd = new SqlCommand("Select U.nom, U.Prenom, T.note, T.texte from dbo.Temoignages T inner join Users U on U.id = T.iduser where T.validation = 1;", con);
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
while (reader.Read())
{
Temoignage temoignage = new Temoignage
{
Nom = reader.GetString(0),
Prenom = reader.GetString(1),
Note = reader.GetInt32(2),
Texte = reader.GetString(3)
};
TemoignageList.Add(temoignage);
}
return TemoignageList;
}
And the ViewModel which receive it
public ObservableCollection<Temoignage> TemoignagesList { get; set; }
....
public void Temoignages()
{
BasicHttpBinding binding = CreateBasicHttp();
this.client1 = new BienEtreServiceClient(binding, EndPoint);
this.instance = ((IBienEtreService)client1.InnerChannel);
client1.GetTemoignageCompleted += ClientOnGetTemoignageCompleted;
client1.GetTemoignageAsync();
}
public void ClientOnGetTemoignageCompleted(object sender, GetTemoignageCompletedEventArgs e)
{ // The problems starts here
ObservableCollection<Temoignage> TemTest = e.Result;
foreach (Temoignage item in TemTest)
{
TemoignagesList.Add(item);
}
}
I thought i could, in ClientOnGetTemoignageCompleted, just take the ObservableCollection i got from WCF and add the elements in my already existing TemoignagesList (which is binded to XAML).
But nope. Even if the Models contains same elements in both projects.
e.Result containt elements i need, i just don't know how to read them.
equals to this when ClientOnGetTemoignageCompleted is executed
Am i missing an important point?
Thanks for help
I think i found it myself, using the models of my wcf service and not the client ones
//using BienEtre.Models;
using BienEtreWcfService;
Now this works and add elements to the list.
public void ClientOnGetTemoignageCompleted(object sender, GetTemoignageCompletedEventArgs e)
{
foreach (Temoignage item in e.Result)
{
TemoignagesList.Add(item);
}
}
Not sure i understood, but it works!
I'm also using List instead of ObservableCollection.
I'm a noob with C# but I'm trying to create a simple code for connecting to my mysql database (most of the code I got from google). So I have this part where something from the database is selected and stored in a list. Now I want to access this from another class but I have no idea how to do that and I couldn't find the answer on google either (this thread also didn't worked for me: Access List from another class) so can anyone please help me?
Here's the particular code:
public List<string>[] Select()
{
string query = "SELECT * FROM users";
//Create a list to store the result
List<string>[] list = new List<string>[3];
list[0] = new List<string>();
list[1] = new List<string>();
list[2] = new List<string>();
//Open connection
if (this.OpenConnection() == true)
{
//Create Command
MySqlCommand cmd = new MySqlCommand(query, connection);
//Create a data reader and Execute the command
MySqlDataReader dataReader = cmd.ExecuteReader();
//Read the data and store them in the list
while (dataReader.Read())
{
list[0].Add(dataReader["id"] + "");
list[1].Add(dataReader["test"] + "");
list[2].Add(dataReader["balance"] + "");
}
//close Data Reader
dataReader.Close();
//close Connection
this.CloseConnection();
//return list to be displayed
return list;
}
else
{
return list;
}
}
Class containing your original method:
public class YourClass
{
public List<string>[] Select()
{
string query = "SELECT * FROM users";
//Create a list to store the result
List<string>[] list = new List<string>[3];
///
/// you original implementation here
///
}
}
Here Class where you use your method:
public class UsingClass
{
private YourClass _yourClass;
public UsingClass()
{
_yourClass = new YourClass();
}
private void SomeUsingMethod()
{
List<string>[] list = _yourClass.Select();
}
}
Seriously I think the way you've structured the data here, is strange.
You're creating an array of a objects, where the object is a List of string:
List<string>[] list = new List<string>[3];
You want to be thinking in objects; create a class that represents the data; in this case User. Something like this:
public class User
{
public string Id { get; set; }
public string Test { get; set; }
public string Balance { get; set; }
}
So personally I'd do the following:
Create a class that represents each user record.
Create a variable to hold a list of users.
Read from MySql and assign each record to a new User object.
Add the user to the list.
Return the list.
Change the return type of your Select method to be List<User> like this:
public List<User> Select() {
Then amend the rest of the method to create and return list of users.
public List<User> Select() {
List<User> list = new List<User>();
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
User user = new User();
user.Id = dataReader["id"].toString();
user.Test = dataReader["test"].toString();
user.Balance = dataReader["balance"].toString();
list.Add(user);
}
dataReader.Close();
this.CloseConnection();
}
return list;
}
Then you can use your list something like this:
ClassThatContainsSelectMethod yourDBObject = new ClassThatContainsSelectMethod();
List<User> users = yourDBObject.Select();
foreach (User user in users)
{
Console.WriteLine(user.Id, user.Test, user.Balance);
}
This code of course could be better structured with factories and error/null checks but it should get you going in the right direction.
There are multiple ways to share data from classes.One of them is defining a property for the desired list and then accsess it. Lets say you have class data
class DataString
{
private string data = "data";
public string Data
{
get { return data}
set { data = value }
}
}
and
class Program
{
static void Main()
{
DataString dataStr = new DataString();
// Assigning the dataStr property causes the 'set' accessor to be called.
dataStr.Data = "some string";
// Evaluating the Hours property causes the 'get' accessor to be called.
System.Console.WriteLine(dataStr.Data); //this will display "some string"
}
}
You could the same way create a list class with your desired functions and variables and modiffy them with methods and access them with properties.
Edit to save you from reading through this whole post
tldr: an object's fields should not be static unless you want all instances of that object to have the same value for that field
I'm trying to create and populate an ArrayList of Blog objects. I do know the generic way do this:
create ArrayList of Blogs
loop (some condition)
create new Blog
add this Blog to AL
However, when I attempt to do so within the while(datareader.read()) loop, all of the elements in the ArrayList are exactly the same Blog. Specifically, I end up with an ArrayList filled with multiple pointers to the very last Blog object from the database table. Here is my code:
public static ArrayList AllBlogs()
{
SqlDataReader dr = anonPage.ExecuteReader("SELECT * FROM Kristina_Blogs");
ArrayList allBlogs = new ArrayList();
if (dr.HasRows)
{
while (dr.Read())
{
Blog b = new Blog();
//grab a row from Kristina_Blogs and assign those attributes to b
b.setTitle(dr["title"].ToString());
b.setMessage(dr["message"].ToString());
b.setId(dr["id"]);
allBlogs.Add(b);
}
}
dr.Close();
return allBlogs;
}
As I said before, the result of this is an ArrayList filled with pointers to the very last blog from the Kristina_Blogs table. I imagine the ArrayList allBlogs looks like [b, b, b, ... b] and therefore they ALL get updated when I say b.setTitle() etc. But how can this be the case if I am creating a NEW Blog object at the beginning of each iteration?
Here is some extra info that you don't have to read but it might clear up some confusion about the structure of the problem:
Blog object has id, title, and message fields and their respective getter/setters
Kristina_Blogs is a table representing these blogs with columns for id, title, message
The suggestions say to include a tag for my DB engine but I can't find a tag for it: Microsoft SQL Server Management Studio
This code works perfectly when I use an ArrayList of Strings instead of Blogs
Edit: Including the code from Blog class
public class Blog
{
public App myApp;
public static string Title;
public static string Message;
public static int Id;
//constructors
public Blog() { }
public Blog(App App) { this.myApp = App; }
//all getters and setters look like this
public string getTitle() { return Title; }
public void setTitle(string t) { Title = t; }
}
The main problem you have, as I mentioned in comments is your member variables are static, so when you set the value, they change in all instances. you should change your code this way:
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string Message { get; set; }
}
And fill your list this way, don't forget to add using System.Linq;:
var result = new List<Blog>();
var connection = #"your connection string";
var command = "SELECT * FROM Kristina_Blogs";
var adapter = new System.Data.SqlClient.SqlDataAdapter(command, connection);
var dataTable = new DataTable();
//Get data
adapter.Fill(dataTable);
dataTable.Rows.Cast<DataRow>().ToList()
.ForEach(row =>
{
var b = new Blog();
b.Id = row.Field<int>("Id");
b.Title = row.Field<string>("Title");
b.Message = row.Field<string>("Message");
result.Add(b);
});
return result;
Note:
When you create a member static, it is shared between all instances of that calss.
In C# you can use property to get or set values, you don't need to setX or setY, when you get the value of a property, the get code of that property will execute and when you assign a value to a property the set part of it will execute. you can define properties this way:
Property:
private int id;
public int Id
{
get
{
return id;
}
set
{
id = value;
}
}
or more simple:
public int Id { get; set; }
All of the fields in your Blog class are static, meaning they're shared between all object instances. You want them to be instance field (meaning not static) so that each object has its own copy of each of those values.
Remove the static attributes from your class:
public class Blog
{
public App myApp;
public String Title;
public String Message;
public int Id;
//constructors
public Blog() { }
public Blog(App App) { this.myApp = App; }
//all getters and setters look like this
public String getTitle() { return Title; }
public String getMessage() { return Message; }
public void setTitle(String t) { Title = t; }
public void setMessage(String m) { Message = m; }
}
When you use static variables, all instances of an object will contain the same values in those variables. By removing the static keyword, you are allowing different instances of the object to hold different values.
Now, every time you create a blog object, that object's Title and Message etc, will contain its own information.
I would make a quick method to prevent null value from throwing error
public static string GetSafeString(SqlDataReader reader, int index)
{
if (!reader.IsDBNull(index))
return reader.GetString(index);
else
return string.Empty;
}
Replace this code:
while (dr.Read())
{
Blog b = new Blog();
//grab a row from Kristina_Blogs and assign those attributes to b
b.setTitle(dr["title"].ToString());
b.setMessage(dr["message"].ToString());
b.setId(dr["id"]);
allBlogs.Add(b);
}
With This Code:
while (dr.Read())
{
Blog b = new Blog();
//grab a row from Kristina_Blogs and assign those attributes to b
b.setId(dr.GetInt32(0));
b.setTitle(GetSafeString(dr, 1);
b.setMessage(GetSafeString(dr, 2);
allBlogs.Add(b);
}
Where the number is the index of field in the record and assuming "id" is an integer. Also consider moving creation of "Blog" object outside of loop and just change values.
I would like to know if there is a better way to solve this problem that I am overlooking. (I'm looking for a second opinion)
I want to create a generic and easy way to bind objects to database reader queries using "Oracle.DataAccess.Client".
In order to do this I initially wanted to create an object which inherited from OracleCommand; however, OracleCommand is a sealed object.
To deal with this I decided to create an extension method which attempts to map objects to generic columns in the database for each row.
EDIT : In my scenario, I know what the database will look like; however, I will not know where the database is before run time. i.e. The database may have been transferred ahead of time and the end user will specify the credentials for the database at run time.
Here is the implementation:
public static T[] Bind<T>(this OracleCommand oe, Binding binding, CommandBehavior Behavior = CommandBehavior.Default)
{
List<T> ret = new List<T>();
using (var reader = oe.ExecuteReader(Behavior))
{
while (reader.Read())
{
T unknownObj = (T)Activator.CreateInstance(typeof(T));
for (int i = 0; i < binding.GetBindCount(); i++)
{
var propinfo = unknownObj.GetType().GetProperties().ToList();
var prop = propinfo.Find((p) => p.Name == binding.GetBindValue(i, true));
prop.SetValue(unknownObj, reader[binding.GetBindValue(i, false)]);
}
ret.Add(unknownObj);
}
}
return ret.ToArray();
}
}
public class Binding
{
List<BindingMap> _map = new List<BindingMap>();
public void AddBind(String VariableName, String ColumnName)
{
_map.Add(new BindingMap(VariableName, ColumnName));
}
public String GetBindValue(int index, bool IsVariable = true)
{
var a = _map.ToArray();
return (IsVariable) ? a[index].Variable : a[index].Column;
}
public int GetBindCount()
{
return _map.Count;
}
}
public class BindingMap
{
public String Column;
public String Variable;
public BindingMap(String v, String c)
{
Variable = v;
Column = c;
}
}
Is there a better way to do this that I've overlooked, or is this a sound?
The way it would be used in real code is like this :
static void Main()
{
Binding b = new Binding();
b.AddBind("CreatedBy", "Create_by");
using (var Conn = new OracleConnection())
{
Conn.ConnectionString = od.Options.GetConnectionString();
using (var Command = new OracleCommand())
{
Command.Connection = Conn;
Command.CommandText = "Select * From Accounts";
Conn.Open();
var a = Command.Bind<Account>(b);
foreach (Account e in a)
{
Console.WriteLine(e.CreatedBy);
}
}
}
Console.Read();
}
public class Account
{
public String CreatedBy
{
get;
set;
}
}
As a slightly better way, you could designate the bound property like Telerik does: with a Linq expression. Here is the usage. Instead of :
AddBind("CreatedBy", "Created_by");
You would write
AddBind( x => x.CreatedBy, "Created_by");
You get a slightly stronger typing opportunity. The signature of AddBind would be:
public void AddBind<T>(Expression<Func<Account, T>> variable, string columnName) {
// ...
}
But I would not go into the way of generic functions. I'd rather overload a non-generic function :
public void AddBind(Expression<Func<Account, double>> variable, string columnName) {
// Add binding for double
}
public void AddBind(Expression<Func<Account, DateTime>> variable, string columnName) {
// Add binding for DateTime
}
// ...
The type of binding would then be selected according to the type of your mapped object. This prevents you from misnaming your properties, so you keep the possibility of performing name changes in the Account class without breaking your bindings.
The column name has still to be a string, sorry.
Of course, the way then to generalize is to make your BindingMap generic. (Taking your business class as a type parameter)
class BindingMap<BusinessClass> {
// ....
public void AddBind(Expression<Func<BusinessClass, double>> variable, string columnName) {
// Add binding for double
}
public void AddBind(Expression<Func<BusinessClass, DateTime>> variable, string columnName) {
// Add binding for DateTime
}
// ...
};
I leave as an exercice to you the problem of digging the property descriptor out of the expression :)
In an attempt of building a 3-tier architecture c# asp.net application, I've started building a class that is database which is used for the connecting to the database, another class that is City which has a method for each column in the table cities, and a Cities class in which I have the GetCities method that creates a list of City objects and then use the DataSource wizard to set the control to use the data from GetCities().
All I get is blanks in the dropdown list. Any idea why?
public List<City> GetCities()
{
List<City> cities = new List<City>();
Database db = new Database();
SqlConnection conn = db.GetConnection();
String sql = "SELECT * FROM CITIES";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
City c = new City(reader.GetInt32(0), reader.GetString(1).ToString());
cities.Add(c);
}
db.CloseConnection();
return cities;
}
thanks
Did you set the DataTextField, DataValueField properties, and call DataBind?
At this point I would try to get the concept working as simply as possible, and then start adding things back in until you locate the problem. Start with a brand new page, add a DropDownList but don't touch the data source or change any properties, go directly into the codebehind and add this in Page_Load:
DropDownList1.DataValueField = "ID";
DropDownList1.DataTextField = "Name";
DropDownList1.DataSource = new[] {
new { ID = 1, Name = "Alice" },
new { ID = 2, Name = "Mike" },
new { ID = 3, Name = "John" }
};
DropDownList1.DataBind();
Does it work? It does for me. Then try to change DataValueField, DataTextField, and DataSource to work with your customer list. Is it broken now? Then you know the problem is in the customer list somewhere, not with the way you're binding the data.
Have you called DataBind() method on the object you want to be populated ?
The issue resided in the City class which after a close inspection I've realised that the constructor was assigning the parameter received incorrectly. It's now working. Thanks!
public class City
{
int id;
string name;
public City(int id, string name)
{
this.id = id;
this.name = name;
}
public int Id
{
get { return id; }
set { id = value; }
}
public String Name
{
get { return name; }
set { name = value; }
}
}