How can I get a selected item in a row from tableview in javaFX with Sqlite Database.
I am now using this to get the Index of the row:
(...)
#FXML
private ObservableList<UserData> data;
#FXML
private TableView table;
#FXML
private void pressTableAction() {
System.out.println("Selected index: " +table.getSelectionModel().getSelectedIndex());
}
(...)
public void initialize (URL url, ResourceBundle rb) {
try {
con = DataBaseConnection.getConnected();
stat = con.createStatement();
data = FXCollections.observableArrayList();
ResultSet rs = con.createStatement().executeQuery("SELECT * FROM person");
while (rs.next()) {
data.add(new UserData(rs.getInt("p_id"), rs.getString("Name")));
}
column1.setCellValueFactory(new PropertyValueFactory("p_id"));
column2.setCellValueFactory(new PropertyValueFactory("Name"));
table.setItems(null);
table.setItems(data);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error on Building Data");
}
Public static class UserData {
private IntegerProperty p_id;
private StringProperty Name;
private UserData(Integer p_id, String Name) {
this.p_id = new SimpleIntegerProperty (p_id);
this.Name = new SimpleStringProperty(Name);
}
public IntegerProperty p_idProperty() {
return p_id;
}
public StringProperty NameProperty() {
return Name;
}
}
My db looks like this:
CREATE TABLE person (p_id INTEGER PRIMARY KEY AUTOINCREMENT, Name VARCHAR(30) NOT NULL);
How can I get the p_id or the Name of the row I clicked?
#FXML
private void pressTableAction() {
System.out.println("Selected index: " + table.getSelectionModel().getSelectedIndex());
System.out.println("Selected p_id: " + ????)
}
First, do not use raw types for your table and table columns. Your IDE should be generating lots of warnings for this. So you should do
#FXML
TableView<UserData> table ;
instead of the declaration you have. Similarly the columns should be declared with the appropriate types.
If your model class UserData follows the standard JavaFX properties pattern, it will have a getP_id() method, and you can do
UserData selected = table.getSelectionModel().getSelectedItem();
System.out.println("Selected p_id: "+selected.getP_id());
In my WPF window, I have a list view (lvw1) of 3 columns. While adding a new list view item, I want to check if item doesn't exist already. I'm using the following line of code
if (!lvw1.Items.Containskey(keyitem))
Keyitem is the string cross-checked with existing items. I guess Containskey method doesn't work in wpf.
Please suggest alternative/appropriate code.
You can use the Contains() method, just assign the new object you would create to a variable and check if it exists already in the ListView:
var newObject = new { Col1 = txt1.Text, Col2 = txt2.Text, Col3 = txt3.Text };
if(!lvw1.Items.Contains(newObject)){
lvw1.Items.Add(newObject);
}
Update to make it ignore the case:
Probably the easiest way to do that is to create a class for your type and override Equals.
This works because ItemCollection's (the type of Items) Contains method uses the Equals method internally to check equality of the containing objects.
public class Item
{
public string Text1 { get; set; }
public string Text2 { get; set; }
public string Text3 { get; set; }
public Item(string text1, string text2, string text3)
{
this.Text1 = text1;
this.Text2 = text2;
this.Text3 = text3;
}
public override bool Equals(object obj)
{
var compareObject = obj as Item;
if (compareObject == null) return false;
return Text1.ToLower().Equals(compareObject.Text1.ToLower()) &&
Text2.ToLower().Equals(compareObject.Text2.ToLower()) &&
Text3.ToLower().Equals(compareObject.Text3.ToLower());
}
}
Then, instead of creating an anonymous object, create an instance of this type.
var newObject = new Item(txt1.Text, txt2.Text, txt3.Text); //use new Item() here
if(!lvw1.Items.Contains(newObject)){
lvw1.Items.Add(newObject);
}
You can find the item by text:
ListViewItem item = lvw1.FindItemWithText("item text");
if (item == null)
{
// does not exist. add to list
}
You could use the ItemCollection.IndexOf Method like this:
if (lvw1.Items.IndexOf(keyitem) < 0)
{
// add the object
}
Note this will implicitely use the keyitem object's Equals method, whatever that does. If it's not ok, then you'll have to enumerate the whole collection (using Linq methods for example) for something that suits your needs.
Try this
using System.Linq;
...
object o = new { Col1 = txt1.Text, Col2 = txt2.Text, Col3 = txt3.Text });
if (!lvw1.Items.Cast<dynamic>().Any(d => IsEqual(d, o))) {
// add item
}
...
private static bool IsEqual(dynamic a, dynamic b) {
return a.Col1 == b.Col1 && a.Col2 == b.Col2 && a.Col3 == b.Col3;
}
I have created several Datagrids dynamically. In the mode of dynamic Datagrid creation the number and name of columns are not known in advance.
So, I created a DataGrid like below :
DataGrid grid = new DataGrid();
grid.Columns.Add(new DataGridTextColumn() { Header = randomHeader1 });
grid.Columns.Add(new DataGridTextColumn() { Header = randomHeader2 });
grid.Columns.Add(new DataGridTextColumn() { Header = randomHeader3 });
// The number of columns for each datagrid is variable
How can I add rows to my Datagrid? For instance, I want to add those 3 rows to Dtatagrid that I have defined in the top:
a, b, c
d, e, f
g, h, i
I tried to solve my problem like below but it is not working:
grid.Items.Add(new Object[] { "a", "b", "c" });
grid.Items.Add(new Object[] { "d", "e", "f" });
grid.Items.Add(new Object[] { "g", "h", "i" });
Please review the code and provide me solution.
Using reflection, you could create a collection of dynamic objects, then bind your DataGrid to it.
There is a great answer here about creating dynamic classes:
How to dynamically create a class in C#?
Using this, you can create the list of properties (which will be columns in your datagrid) at runtime like this
List<TypeBuilderNamespace.Field> myFields = new List<TypeBuilderNamespace.Field>();
myFields.Add( new TypeBuilderNamespace.Field("FirstName", Type.GetType("System.String")));
myFields.Add( new TypeBuilderNamespace.Field("Surname", Type.GetType("System.String")));
myFields.Add( new TypeBuilderNamespace.Field("Age", Type.GetType("System.Int32")));
Then dynamically create your class
Type myDynamicType = TypeBuilderNamespace.MyTypeBuilder.CompileResultType(myFields);
Create some sample data
List<dynamic> people = new List<dynamic>();
dynamic person1 = Activator.CreateInstance(myDynamicType);
person1.FirstName = "John";
person1.Surname = "Smith";
person1.Age = 45;
people.Add(person1);
dynamic person2 = Activator.CreateInstance(myDynamicType);
person2.FirstName = "Emma";
person2.Surname = "Jones";
person2.Age = 18;
people.Add(person2);
Then bind your data to the grid
DataGrid grid = new DataGrid();
grid.AutoGenerateColumns = true;
grid.ItemsSource = people;
This gives the following DataGrid
I modified the code in the answer quoted above slightly, so here is my listing of that for completeness:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace TypeBuilderNamespace {
public class Field {
public String FieldName { get; set; }
public Type FieldType { get; set; }
public Field(String name, Type type) {
FieldName = name;
FieldType = type;
}
}
public static class MyTypeBuilder {
public static Type CompileResultType(List<Field> fields) {
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
FieldName(string) and FieldType(Type)
foreach (var field in fields)
CreateProperty(tb, field.FieldName, field.FieldType);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder() {
var typeSignature = "MyDynamicType";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return tb;
}
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType) {
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
}
This is my object structure
class object
{
string projectname;
string projectid;
list<string> associated_students;
}
//The List I am binding to the grid
list<objects> objectList = getList();
dataGridView.Source =objectList;
Now I want to bind the combo box inside the datagrid with the list "associated_students"
If I understand the question, you want each row to be tied to an object within your list of objects and you want the third column to show a combobox of that object's unique list of associated students. If I am correct, a simple search leads to this similar question:
How do I set up a DataGridView ComboBoxColumn with a different DataSource in each cell?
To solve, you need to manually bind each row. I was able to duplicate your problem and came up with this solution:
Your class "object"
public class Assignment
{
public Assignment()
{
this.Associated_Students = new List<string>();
}
public string ProjectName { get; set; }
public string ProjectID { get; set; }
public List<string> Associated_Students { get; set; }
}
And in Form1:
public Form1()
{
InitializeComponent();
this.Assignments = new List<Assignment>()
{
new Assignment()
{
ProjectID = "1",
ProjectName = "First",
Associated_Students = new List<string>() { "Me", "You", "Him", "Her" }
},
new Assignment()
{
ProjectID = "2",
ProjectName = "Second",
Associated_Students = new List<string>() { "Foo", "Bar" }
}
};
this.BindDGViewToList();
}
public List<Assignment> Assignments { get; set; }
public void BindDGViewToList()
{
DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn();
col1.Name = "Project Name";
col1.ValueType = typeof(string);
dataGridView1.Columns.Add(col1);
DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();
col2.Name = "Project ID";
col2.ValueType = typeof(string);
dataGridView1.Columns.Add(col2);
DataGridViewComboBoxColumn col3 = new DataGridViewComboBoxColumn();
col3.Name = "Associated Students";
col3.ValueType = typeof(string);
dataGridView1.Columns.Add(col3);
for (int i = 0; i < this.Assignments.Count; i++)
{
DataGridViewRow row = (DataGridViewRow)(dataGridView1.Rows[0].Clone());
DataGridViewTextBoxCell textCell = (DataGridViewTextBoxCell)(row.Cells[0]);
textCell.ValueType = typeof(string);
textCell.Value = this.Assignments[i].ProjectName;
textCell = (DataGridViewTextBoxCell)(row.Cells[1]);
textCell.ValueType = typeof(string);
textCell.Value = this.Assignments[i].ProjectID;
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)(row.Cells[2]);
comboCell.ValueType = typeof(string);
comboCell.DataSource = this.Assignments[i].Associated_Students;
dataGridView1.Rows.Add(row);
}
}
Note: This will display what you are asking for but you will have to handle updating your data. I would suggest researching BindingList over List objects. There may be better solutions, but this worked quickly for me.
I am new to the Dapper micro ORM. So far I am able to use it for simple ORM related stuff but I am not able to map the database column names with the class properties.
For example, I have the following database table:
Table Name: Person
person_id int
first_name varchar(50)
last_name varchar(50)
and I have a class called Person:
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Please note that my column names in the table are different from the property name of the class to which I am trying to map the data which I got from the query result.
var sql = #"select top 1 PersonId,FirstName,LastName from Person";
using (var conn = ConnectionFactory.GetConnection())
{
var person = conn.Query<Person>(sql).ToList();
return person;
}
The above code won't work as the column names don't match the object's (Person) properties. In this scenario, is there anything i can do in Dapper to manually map (e.g person_id => PersonId) the column names with object properties?
Dapper now supports custom column to property mappers. It does so through the ITypeMap interface. A CustomPropertyTypeMap class is provided by Dapper that can do most of this work. For example:
Dapper.SqlMapper.SetTypeMap(
typeof(TModel),
new CustomPropertyTypeMap(
typeof(TModel),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName))));
And the model:
public class TModel {
[Column(Name="my_property")]
public int MyProperty { get; set; }
}
It's important to note that the implementation of CustomPropertyTypeMap requires that the attribute exist and match one of the column names or the property won't be mapped. The DefaultTypeMap class provides the standard functionality and can be leveraged to change this behavior:
public class FallbackTypeMapper : SqlMapper.ITypeMap
{
private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;
public FallbackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
{
_mappers = mappers;
}
public SqlMapper.IMemberMap GetMember(string columnName)
{
foreach (var mapper in _mappers)
{
try
{
var result = mapper.GetMember(columnName);
if (result != null)
{
return result;
}
}
catch (NotImplementedException nix)
{
// the CustomPropertyTypeMap only supports a no-args
// constructor and throws a not implemented exception.
// to work around that, catch and ignore.
}
}
return null;
}
// implement other interface methods similarly
// required sometime after version 1.13 of dapper
public ConstructorInfo FindExplicitConstructor()
{
return _mappers
.Select(mapper => mapper.FindExplicitConstructor())
.FirstOrDefault(result => result != null);
}
}
And with that in place, it becomes easy to create a custom type mapper that will automatically use the attributes if they're present but will otherwise fall back to standard behavior:
public class ColumnAttributeTypeMapper<T> : FallbackTypeMapper
{
public ColumnAttributeTypeMapper()
: base(new SqlMapper.ITypeMap[]
{
new CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName)
)
),
new DefaultTypeMap(typeof(T))
})
{
}
}
That means we can now easily support types that require map using attributes:
Dapper.SqlMapper.SetTypeMap(
typeof(MyModel),
new ColumnAttributeTypeMapper<MyModel>());
Here's a Gist to the full source code.
This works fine:
var sql = #"select top 1 person_id PersonId, first_name FirstName, last_name LastName from Person";
using (var conn = ConnectionFactory.GetConnection())
{
var person = conn.Query<Person>(sql).ToList();
return person;
}
Dapper has no facility that allows you to specify a Column Attribute, I am not against adding support for it, providing we do not pull in the dependency.
For some time, the following should work:
Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true;
I do the following using dynamic and LINQ:
var sql = #"select top 1 person_id, first_name, last_name from Person";
using (var conn = ConnectionFactory.GetConnection())
{
List<Person> person = conn.Query<dynamic>(sql)
.Select(item => new Person()
{
PersonId = item.person_id,
FirstName = item.first_name,
LastName = item.last_name
}
.ToList();
return person;
}
Here is a simple solution that doesn't require attributes allowing you to keep infrastructure code out of your POCOs.
This is a class to deal with the mappings. A dictionary would work if you mapped all the columns, but this class allows you to specify just the differences. In addition, it includes reverse maps so you can get the field from the column and the column from the field, which can be useful when doing things such as generating sql statements.
public class ColumnMap
{
private readonly Dictionary<string, string> forward = new Dictionary<string, string>();
private readonly Dictionary<string, string> reverse = new Dictionary<string, string>();
public void Add(string t1, string t2)
{
forward.Add(t1, t2);
reverse.Add(t2, t1);
}
public string this[string index]
{
get
{
// Check for a custom column map.
if (forward.ContainsKey(index))
return forward[index];
if (reverse.ContainsKey(index))
return reverse[index];
// If no custom mapping exists, return the value passed in.
return index;
}
}
}
Setup the ColumnMap object and tell Dapper to use the mapping.
var columnMap = new ColumnMap();
columnMap.Add("Field1", "Column1");
columnMap.Add("Field2", "Column2");
columnMap.Add("Field3", "Column3");
SqlMapper.SetTypeMap(typeof (MyClass), new CustomPropertyTypeMap(typeof (MyClass), (type, columnName) => type.GetProperty(columnMap[columnName])));
An easy way to achieve this is to just use aliases on the columns in your query.
If your database column is PERSON_ID and your object's property is ID, you can just do
select PERSON_ID as Id ...
in your query and Dapper will pick it up as expected.
Taken from the Dapper Tests which is currently on Dapper 1.42.
// custom mapping
var map = new CustomPropertyTypeMap(typeof(TypeWithMapping),
(type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName));
Dapper.SqlMapper.SetTypeMap(typeof(TypeWithMapping), map);
Helper class to get name off the Description attribute (I personally have used Column like #kalebs example)
static string GetDescriptionFromAttribute(MemberInfo member)
{
if (member == null) return null;
var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false);
return attrib == null ? null : attrib.Description;
}
Class
public class TypeWithMapping
{
[Description("B")]
public string A { get; set; }
[Description("A")]
public string B { get; set; }
}
Before you open the connection to your database, execute this piece of code for each of your poco classes:
// Section
SqlMapper.SetTypeMap(typeof(Section), new CustomPropertyTypeMap(
typeof(Section), (type, columnName) => type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false).OfType<ColumnAttribute>().Any(attr => attr.Name == columnName))));
Then add the data annotations to your poco classes like this:
public class Section
{
[Column("db_column_name1")] // Side note: if you create aliases, then they would match this.
public int Id { get; set; }
[Column("db_column_name2")]
public string Title { get; set; }
}
After that, you are all set. Just make a query call, something like:
using (var sqlConnection = new SqlConnection("your_connection_string"))
{
var sqlStatement = "SELECT " +
"db_column_name1, " +
"db_column_name2 " +
"FROM your_table";
return sqlConnection.Query<Section>(sqlStatement).AsList();
}
Messing with mapping is borderline moving into real ORM land. Instead of fighting with it and keeping Dapper in its true simple (fast) form, just modify your SQL slightly like so:
var sql = #"select top 1 person_id as PersonId,FirstName,LastName from Person";
If you're using .NET 4.5.1 or higher checkout Dapper.FluentColumnMapping for mapping the LINQ style. It lets you fully separate the db mapping from your model (no need for annotations)
This is piggy backing off of other answers. It's just a thought I had for managing the query strings.
Person.cs
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public static string Select()
{
return $"select top 1 person_id {nameof(PersonId)}, first_name {nameof(FirstName)}, last_name {nameof(LastName)}from Person";
}
}
API Method
using (var conn = ConnectionFactory.GetConnection())
{
var person = conn.Query<Person>(Person.Select()).ToList();
return person;
}
The simple solution to the problem Kaleb is trying to solve is just to accept the property name if the column attribute doesn't exist:
Dapper.SqlMapper.SetTypeMap(
typeof(T),
new Dapper.CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName) || prop.Name == columnName)));
The easier way (same as #Matt M's answer but corrected and added fallback to default map)
// override TypeMapProvider to return custom map for every requested type
Dapper.SqlMapper.TypeMapProvider = type =>
{
// create fallback default type map
var fallback = new DefaultTypeMap(type);
return new CustomPropertyTypeMap(type, (t, column) =>
{
var property = t.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(typeof(ColumnAttribute))
.Cast<ColumnAttribute>()
.Any(attr => attr.Name == column));
// if no property matched - fall back to default type map
if (property == null)
{
property = fallback.GetMember(column)?.Property;
}
return property;
});
};
for all of you who use Dapper 1.12, Here's what you need to do to get this done:
Add a new column attribute class:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property]
public class ColumnAttribute : Attribute
{
public string Name { get; set; }
public ColumnAttribute(string name)
{
this.Name = name;
}
}
Search for this line:
map = new DefaultTypeMap(type);
and comment it out.
Write this instead:
map = new CustomPropertyTypeMap(type, (t, columnName) =>
{
PropertyInfo pi = t.GetProperties().FirstOrDefault(prop =>
prop.GetCustomAttributes(false)
.OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName));
return pi != null ? pi : t.GetProperties().FirstOrDefault(prop => prop.Name == columnName);
});
I know this is a relatively old thread, but I thought I'd throw what I did out there.
I wanted attribute-mapping to work globally. Either you match the property name (aka default) or you match a column attribute on the class property. I also didn't want to have to set this up for every single class I was mapping to. As such, I created a DapperStart class that I invoke on app start:
public static class DapperStart
{
public static void Bootstrap()
{
Dapper.SqlMapper.TypeMapProvider = type =>
{
return new CustomPropertyTypeMap(typeof(CreateChatRequestResponse),
(t, columnName) => t.GetProperties().FirstOrDefault(prop =>
{
return prop.Name == columnName || prop.GetCustomAttributes(false).OfType<ColumnAttribute>()
.Any(attr => attr.Name == columnName);
}
));
};
}
}
Pretty simple. Not sure what issues I'll run into yet as I just wrote this, but it works.
Kaleb Pederson's solution worked for me. I updated the ColumnAttributeTypeMapper to allow a custom attribute (had requirement for two different mappings on same domain object) and updated properties to allow private setters in cases where a field needed to be derived and the types differed.
public class ColumnAttributeTypeMapper<T,A> : FallbackTypeMapper where A : ColumnAttribute
{
public ColumnAttributeTypeMapper()
: base(new SqlMapper.ITypeMap[]
{
new CustomPropertyTypeMap(
typeof(T),
(type, columnName) =>
type.GetProperties( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(prop =>
prop.GetCustomAttributes(true)
.OfType<A>()
.Any(attr => attr.Name == columnName)
)
),
new DefaultTypeMap(typeof(T))
})
{
//
}
}