I have Model like this
interface IStudent {
string Name;
List<Subjects> Marks;
int RollNumber;
}
class ViewModel {
ObservableCollection<IStudent> FromExcel;
ObservableCollection<IStudent> FromDB;
}
I need to bind the union of both collection on UI. Whats the best way. I was thinking of having another property ObservableCollection<IStudent> FromBoth; generated using LINQ Union method with comparer. My question is
Is it fine to have three collection to bind on UI? Note: I need to remove duplicates, giving priority to data from excel.
I need to pick some data from DB rather than excel in certain case.
For example: name="hungrymind" in fromExcel and name="hungrymind concepts" on fromDB collection. By default, grid on UI should show hungrymind (priority to excel), but if user uncheck column(aka property) from UI, then priority to data for that column becomes DB, i.e, UI should show "hungrymind concepts"
What should be approach to achieve this. My approach would be on user event, pick data from FromDB or FromExcel for each items in the collection and assign it to property in FromBoth collection. Since there are more than 100 columns, I had to use reflection, but wouldn't be slow down the performance? If I avoid reflection, then I have to write a method for each column. Any suggestion on pattern or approach ?
I solved the issue like this
interface IStudent {
string Name { get; set; }
List<Subjects> Marks { get; set; }
int RollNumber { get; set; }
}
class EntityViewModel: IStudent {
IStudent FromExcel;
IStudent FromDB;
public string Name {
get { return Choose("Name").Name; }
set { Choose("Name").Name = value; }
}
public string RollNumber{
get { return Choose("RollNumber").RollNumber; }
set { Choose("RollNumber").RollNumber = value; }
}
internal IStudent Choose(string propertyName){
if(IsOveridable(propertyName))
return this.FromExcel;
else
return this.FromDB
}
}
class ViewModel{
ObservableCollection<EntityViewModel> Entities;
}
In that case why don't you build a meta-model which would help you in organizing the data, like for instance
String objectName
String dataType
String defaultName
String displayName
String userSelectedName
boolean isUserOvverride
String viewType // (i.e. Text Input, Combo Box, Text Area, Radio Button, Multi Line List)
String viewElementTypeId // (i.e. for Combo Box,Radio Button this refers to user options available and for Text Input or Area it would be null)
Though the above approach decreases the performance but you can adopt to any number of types that might come in tomorrow.
Related
This has baffled me for a while now and I cannot seem to get the grasp of it. I'm using Cell Value Factory to populate a simple one column table and it does not populate in the table.
It does and I click the rows that are populated but I do not see any values in them- in this case String values. [I just edited this to make it clearer]
I have a different project under which it works under the same kind of data model. What am I doing wrong?
Here's the code. The commented code at the end seems to work though. I've checked to see if the usual mistakes- creating a new column instance or a new tableview instance, are there. Nothing. Please help!
//Simple Data Model
Stock.java
public class Stock {
private SimpleStringProperty stockTicker;
public Stock(String stockTicker) {
this.stockTicker = new SimpleStringProperty(stockTicker);
}
public String getstockTicker() {
return stockTicker.get();
}
public void setstockTicker(String stockticker) {
stockTicker.set(stockticker);
}
}
//Controller class
MainGuiController.java
private ObservableList<Stock> data;
#FXML
private TableView<Stock> stockTableView;// = new TableView<>(data);
#FXML
private TableColumn<Stock, String> tickerCol;
private void setTickersToCol() {
try {
Statement stmt = conn.createStatement();//conn is defined and works
ResultSet rsltset = stmt.executeQuery("SELECT ticker FROM tickerlist order by ticker");
data = FXCollections.observableArrayList();
Stock stockInstance;
while (rsltset.next()) {
stockInstance = new Stock(rsltset.getString(1).toUpperCase());
data.add(stockInstance);
}
} catch (SQLException ex) {
Logger.getLogger(WriteToFile.class.getName()).log(Level.SEVERE, null, ex);
System.out.println("Connection Failed! Check output console");
}
tickerCol.setCellValueFactory(new PropertyValueFactory<Stock,String>("stockTicker"));
stockTableView.setItems(data);
}
/*THIS, ON THE OTHER HAND, WORKS*/
/*Callback<CellDataFeatures<Stock, String>, ObservableValue<String>> cellDataFeat =
new Callback<CellDataFeatures<Stock, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call(CellDataFeatures<Stock, String> p) {
return new SimpleStringProperty(p.getValue().getstockTicker());
}
};*/
Suggested solution (use a Lambda, not a PropertyValueFactory)
Instead of:
aColumn.setCellValueFactory(new PropertyValueFactory<Appointment,LocalDate>("date"));
Write:
aColumn.setCellValueFactory(cellData -> cellData.getValue().dateProperty());
For more information, see this answer:
Java: setCellValuefactory; Lambda vs. PropertyValueFactory; advantages/disadvantages
Solution using PropertyValueFactory
The lambda solution outlined above is preferred, but if you wish to use PropertyValueFactory, this alternate solution provides information on that.
How to Fix It
The case of your getter and setter methods are wrong.
getstockTicker should be getStockTicker
setstockTicker should be setStockTicker
Some Background Information
Your PropertyValueFactory remains the same with:
new PropertyValueFactory<Stock,String>("stockTicker")
The naming convention will seem more obvious when you also add a property accessor to your Stock class:
public class Stock {
private SimpleStringProperty stockTicker;
public Stock(String stockTicker) {
this.stockTicker = new SimpleStringProperty(stockTicker);
}
public String getStockTicker() {
return stockTicker.get();
}
public void setStockTicker(String stockticker) {
stockTicker.set(stockticker);
}
public StringProperty stockTickerProperty() {
return stockTicker;
}
}
The PropertyValueFactory uses reflection to find the relevant accessors (these should be public). First, it will try to use the stockTickerProperty accessor and, if that is not present fall back to getters and setters. Providing a property accessor is recommended as then you will automatically enable your table to observe the property in the underlying model, dynamically updating its data as the underlying model changes.
put the Getter and Setter method in you data class for all the elements.
I have a Custom control inheriting from Control class in my WinForm. My control contains multiple panels and other UIElements.
This is what my control is supposed to look like
There's a database panel,
database panel contains a single checkbox only.
and there's a Server panel,
server panel contains many database panels and a single label; the header label.
And finally there's the container panel that contains all my Server panels.
I found this Item Collection option for a User Control but I couldn't really understand the accepted answer on it. If someone could help explain it better that would be great.
Also, if someone could just put some links for creating advanced custom controls. I've been reading all day about it and I still can't make any sense of it all. Is there a step-by-step guide for advanced custom controls?
[Edit]
Basically what I need is to create a custom collection within my custom control. Currently my control is built as Winform Control Library which I build and then I use in my main program later.
So in my main program, I can just drag and drop the component on my form and use it.
By default, the custom control will load with one Server that contains one database.
What I want is to be able to add/remove other databases/servers to it if I need to, in my MAIN program
I'm having trouble explaining exactly what I need because I simply do not understand how the whole custom control/items collection thing works really, and i'm sorry for that. I would really appreciate some links that explains this stuff clearly
here's my code for this control:
This code only creates my default control, but I am UNABLE to add to it. The collection property appears in my property windows but when I add items to it and click okay nothing happens.
public class Database : System.Windows.Forms.Panel
{
public CheckBox _ckbDatabase;
public Database()
{
_ckbDatabase = new CheckBox();
this.BackColor = _pnlDatabaseBackColor;
this.Size = _pnlDatabaseSize;
this.AutoSize = false;
this.Height = 40;
this.Width = 200;
this.Location = _pnlDatabaseLocation;
_ckbDatabase.Top = 10;
_ckbDatabase.Left = 15;
_ckbDatabase.TextAlign = _ckbdbTextAlignment;
_ckbDatabase.Font = _ckbdbFont;
_ckbDatabase.ForeColor = Color.White;
this.Controls.Add(_ckbDatabase);
}
#Propterties
}
public class Server : System.Windows.Forms.Panel
{
private Label _lblserver;
private Database database;
public Server()
{
_lblserver = new Label();
database = new Database();
this.BackColor = _pnlServerBackColor;
this.Size = _pnlServerSize;
this.AutoSize = false;
_lblserver.Dock = _lblserverDock;
_lblserver.Font = _lblsrvFont;
_lblserver.BackColor = _lblServerBackColor;
_lblserver.AutoSize = false;
_lblserver.Text = SRV;
database.Top = 35;
database._ckbDatabase.Text = DB;
this.Controls.Add(_lblserver);
this.Controls.Add(database);
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public DatabaseCollection DatabaseCollection { get; set; }
#Propterties
}
public class ServersCollection : CollectionBase
{
public Server this[int index]
{
get { return (Server)List[index]; }
}
public void Add(Server server)
{
List.Add(server);
}
public void Remove(Server server)
{
List.Remove(server);
}
}
How about something simple like this:
public class Server {
public string Name { get; set; }
public List<Database> Databases { get; set; }
public Server() {
Databases = new List<Database>();
}
}
public class Database {
public string Name { get; set; }
public bool Enabled { get; set; }
}
Then you can just add it like this:
List<Server> servers = new List<Server>();
Server serverA = new Server { Name = "Server A" };
serverA.Databases.Add(new Database { Name = "Database 1", Enabled = true });
serverA.Databases.Add(new Database { Name = "Database 2", Enabled = false });
Server serverB = new Server { Name = "Server B" };
serverB.Databases.Add(new Database { Name = "Database 1", Enabled = false });
serverB.Databases.Add(new Database { Name = "Database 2", Enabled = false });
servers.Add(serverA);
servers.Add(serverB);
When you link to the Item Collection part it seemed like you wanted to be able to add servers and databases in design mode but then you mention you want to do it by code? If this is not what you want you need to give us more information.
Looks to me like you are mostly there. First off, here's a more complete collection class:
public class ServersCollection : IEnumerable<Server>
{
private List<Server> _servers = new List<Server>();
public Server this[int index]
{
get { return _servers[index]; }
}
public IEnumerator<Server> GetEnumerator()
{
foreach (var server in _servers)
yield return server;
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public void Add(Server server)
{
_servers.Add(server);
}
public void Remove(Server server)
{
//You might consider a deliberate loop to evaluate a proper match
//Don't forget to Dispose() it first!
_servers.Remove(server);
}
public void Clear()
{
for (Int32 i = _servers.Count - 1; i >= 0; i--)
_servers[i].Dispose();
_servers.Clear();
}
}
Add an instance of the ServersCollection class to the container control, the one at the top level that holds server panels:
private ServersCollection _servers = new ServersCollection();
public ServersCollection Servers { get { return _servers; } }
Use that as a way for it to add Server controls to its own collection of controls.
Do a similar thing with the DatabaseCollection in the Server class, again so that it can add Database panels to its controls collection.
Then, wherever you have an instance of a control, you will also have access to the collection of what it holds:
myControl.Servers
//-or-
myServer.Databases
...allowing you to add/remove, as such:
myControl.Servers.Add(new Server());
//-or-
myServer.Databases.Add(new Database());
Points of emphasis
Your classes are controls, but they also own other controls. Proper use of the Dispose pattern will be crucial or you'll have memory issues throughout.
I would remove these lines, they don't matter unless you intend to add servers/DBs at form design time (i.e. fixed entries or defaults):
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public DatabaseCollection DatabaseCollection { get; set; }
Finally, you could (should!) take that collection class further, with overloads for Add() and Remove() that do a better job of deciding when/how/what to do based on more than an instance, e.g. by name? You could also add another indexer to fetch by name, for instance, instead of just index (which you might not readily know).
How do I search into my collection ??
Can't get it working... Don't I just have to do :
Contacts c = new Contacts();
if (c.Contact_name == "Test") {
MessageBox.Show("exists!");
}
Does not work :-)
public ObservableCollection<Contacts> contacts = new ObservableCollection<Contacts>();
class Contacts
{
public string Contact_id { get; set; }
public string Contact_name { get; set; }
}
You're setting c to a new instance of Contacts which does not have the Contact_name property set to anything...
If you're trying to search a collection for a specific contact, the easiest way would probably be to use the following Linq statement, which will return the first object in the collecting matching your condition, or null if no object is found
contacts.FirstOrDefault(p => p.Contact_name == "Test");
There's other Linq extensions that may be better suited for you depending on what you want too, such as .Exists() if you only want to know if an item exists or not
If you're not using Linq, the easiest way would be with a loop
foreach(var c in contacts)
{
if (c.Contact_name == "Test") {
MessageBox.Show("exists!");
}
}
the problem is pretty easy if you reduce it to one class. Given the following image, I want to create a simple two sided control, which puts items from one list into the other depending on a boolean value.
EDIT: You can of course click on items in both lists, and the item switches to the other list. Also, a callback is called, in case I need to update some DB stuff...
I created a nice picture to movivate you a little bit, because I am stuck...
The world is not so simple like the example: How would you solve this for various classes.
Imagine a class like "Car" with "IsFast". Or a class like "Fruits" with "ILikeIt". I do not want to reprogramm the WPF control each time, I need some way to bind... (oh, I think I just got an idea)... but still, is there any good practice how to allow generic classes (like T) as long as they implement certain properties.. Or a wrapper class?
I have no idea, how would you solve it. Simple binding with OnClick Functions seems not enough... Not sure... And, by the way, "write 3 controls" IS a suitable answer. If it is simpler, just tell me.
alt text http://img31.imageshack.us/img31/96/listexample.png
I think I understand what you are after, this should get you started.
I am assuming that your usercontrol has two listviews, one intended for the true items named "TrueList", the other for the false items named "FalseList".
Extend your usercontrol from ItemsCollection and bind the ItemsSource property of each listview to the ItemsSource of the parent usercontrol.
Add a TrueFilter and a FalseFilter property to your usercontrol:
Predicate<object> trueFilter;
public Predicate<object> TrueFilter
{
get
{
return trueFilter;
}
set
{
if (trueFilter!= null && this.TrueList.Items != null)
this.TrueList.Items.Filter -= trueFilter;
trueFilter = value;
if (trueFilter!= null && this.TrueList.Items != null)
this.TrueList.Items.Filter += trueFilter;
}
}
Predicate<object> falseFilter;
public Predicate<object> FalseFilter
{
get
{
return falseFilter;
}
set
{
if (falseFilter!= null && this.FalseList.Items != null)
this.FalseList.Items.Filter -= falseFilter;
filter = value;
if (falseFilter!= null && this.FalseList.Items != null)
this.FalseList.Items.Filter += falseFilter;
}
}
Then create an "IToggle" (or some other more meaningful name) interface:
public interface IToggle
{
Predicate<object> TrueFilter { get; }
Predicate<object> FalseFilter { get; }
}
Then, extend ObservableCollection for each of your custom classes, implementing the "IToggle" interface:
public class Cars : ObservableCollection<Car>, IToggle
{
Predicate<object> trueFilter;
public Predicate<object> TrueFilter
{
get
{
if (trueFilter == null)
trueFilter = new Predicate<object>(this.TrueFilterPredicate);
return trueFilter;
}
}
private bool TrueFilterPredicate(object value)
{
Car car = (Car)value;
return car.IsFast;
}
Predicate<object> falseFilter;
public Predicate<object> FalseFilter
{
get
{
if (falseFilter == null)
falseFilter = new Predicate<object>(this.FalseFilterPredicate);
return falseFilter;
}
}
private bool FalseFilterPredicate(object value)
{
Car car = (Car)value;
return !car.IsFast;
}
Next, override the ItemsSource property on your user control:
public new IEnumerable ItemsSource
{
get { return base.ItemsSource; }
set
{
if (value != null && !(value is IToggle))
throw new Exception("You may only bind this control to collections that implement IToggle.");
base.ItemsSource = value;
this.TrueFilter = base.ItemsSource == null ? null : (base.ItemsSource as IToggle).TrueFilter;
this.FalseFilter = base.ItemsSource == null ? null : (base.ItemsSource as IToggle).FalseFilter;
}
}
Finally, call TrueList.Items.Refresh() and FalseList.Items.Refresh() on your event callbacks to refresh the item views whenever you switch an item from true to false, and vice versa.
This solution still requires writing some implementation code for each custom class (the true and false filters), but it should keep the extra code to a minimum.
Alternatively, it would be a much simpler solution if you gave each of your custom classes a common interface, something like:
public interface Valid
{
bool IsValid { get; set; }
}
Then you could use a single set of filters (or style setters, or databinding with converters) to work against the "Valid" interface. Instead of "Car.IsFast" and "Fruit.ILikeIt" you would use "Car.IsValid" and "Fruit.IsValid".
How would I do it?
Create a control called PickList that subclasses ItemsControl and includes commands for picking a single item, picking all items, unpicking all items etcetera
Create a class called PickListItem that has an IsPicked property
Define the control template for PickList to include two ListBoxes and a bunch of buttons for picking one, all etcetera. The template would include a couple of CollectionViewSources to segregate those items that are picked (which would be on the right) from those that are not (which would be on the left)
You would then use this control just like any other ItemsControl, and reuse it for any data type you might have:
<PickList ItemsSource="{Binding People}">
<PickList.ItemContainerStyle>
<Style TargetType="{x:Type PickListItem}">
<Setter Property="IsPicked" Value="{Binding IsRich}"/>
</Style>
</PickList.ItemContainerStyle>
</PickList>
make a user control. add a SourceList dep. Property. Add a delegate property for your callback, add a delegate property for your "is in list" filter
use two ListBox controls ( a left and a right ), use CollectionViewSource to set the source of the two lists, using the filter delegate to determine membership.
I've been using WinForms databinding to display data from a database mapped with Fluent NHibernate, and that's been working great.
For example, I can just set a DataGridView's DataSource property from an entity's IList property, and voila - there's all the data!
But now I need to start adding and saving new data rows, and that's not going so well. I thought I'd be able to just enable the grid's AllowUserToAddRows property, and new rows would get added to the underlying IList in the entity, but that didn't work.
Then, after a little searching, I tried setting the DataSource property to a BindingList that was populated from the IList, but that's not being updated with new rows either.
During the course of my searches, I also came upon a few people reporting difficulty with WinForms and DataBinding in general, which makes me wonder if I should pursue that approach any further.
Is the DataBinding approach worth continuing? If so, can anyone suggest where I'm going wrong?
Or is it better to just handle all the DataGridView events associated with adding a new row, and writing my own code to add new objects to the IList property in my entity?
Other suggestions? (though I don't think switching to WPF is going to be an option, no matter how much better the databinding may be)
Can you load (or copy) your nHibernate entities into a generic List? If so, I have had good success in with two-way binding using a DataGridView bound to a generic List.
The key points are:
The generic list contains list objects where each is an instance of your custom class.
Your custom class must implement public properties for each of the fields to bind. Public fields didn't work for me.
Use a BindingSource to wrap the actual generic list.
The BindingSOurce allows you to set the AllowNew property to true. Binding directly to the List almost works, but the DataGridVieww does not display the "New row" line, even if AllowUsersToAddRows = true.
For example, add this code to a Form with a dataGridView1:
private List<MyObject> m_data = new List<MyObject>();
private BindingSource m_bs =new BindingSource();
private void Form1_Load(object sender, EventArgs e)
{
m_data.Add(new MyObject(0,"One",DateTime.Now));
m_data.Add(new MyObject(1, "Two", DateTime.Now));
m_data.Add(new MyObject(2, "Three", DateTime.Now));
m_bs.DataSource = m_data;
m_bs.AllowNew = true;
dataGridView1.DataSource = m_bs;
dataGridView1.AutoGenerateColumns = true;
dataGridView1.AllowUserToAddRows = true;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
for (int i = 0; i < m_data.Count ; i++)
{
Console.WriteLine(string.Format("{0} {1} {2}", m_data[i].ID, m_data[i].Name, m_data[i].DOB));
}
}
}
public class MyObject
{
// Default ctor, required for adding new rows in DataGridView
public MyObject()
{
}
public MyObject(int id, string name, DateTime dob)
{
ID = id;
Name = name;
DOB = dob;
}
private int m_id;
public int ID
{
get
{
return m_id;
}
set
{
m_id = value;
}
}
private string m_name;
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
}
}
private DateTime m_dob;
public DateTime DOB
{
get
{
return m_dob;
}
set
{
m_dob = value;
}
}
}
When the form closes, the contents of the bound List are printed to the Output window.