I have a DateNavigatorViewModel + DateNavigatorView on my ButtonBar.
Below are 2 Views which get exchanged:
DailyView and WeeklyView. Each View has a DailyViewModel and WeeklyViewModel.
In my DateNavigatorViewModel I have messenger.Send(SelectedDate);
In my DailyViewModel and WeeklyViewModel each register in the constructor:
messenger.Register<DateTime>(this, LoadDailyData);
messenger.Register<DateTime>(this, LoadWeeklyData);
guess what happens when I select a date...
I am using MVVM Light toolkit.
How can I solve that problem of getting 2 times data from database?
In your data access layer you could use caching stored in some static dictionary, load all the data you need from the database for both view and filter at the data layer for the individual viewsmodels.
Or an alternative, have the DateChanged messege be received by the Data objects, load the data and then have a second message raised and received by your two views.
MainViewModel is instantiated then:
clicking on the Daily-Button instantiates the:
public DailyViewModel(IMessenger messenger)
{
_messenger = messenger;
_messenger.Register<DateNavigatorInfoObject>(this, LoadDailyData);
}
private void LoadDailyData(DateNavigatorInfoObject infoObject)
{
if (infoObject.DateNavigatorMode != DateTest.DateMode.Day)
return;
// get daily database stuff
}
After the DateNavigatorViewModel got instantiated see BELOW
clicking on the Weekly-Button instantiates the:
public WeeklyViewModel(IMessenger messenger)
{
_messenger = messenger;
_messenger.Register<DateNavigatorInfoObject>(this, LoadWeeklyData);
}
private void LoadWeeklyData(DateNavigatorInfoObject infoObject)
{
if (infoObject.DateNavigatorMode != DateTest.DateMode.Week)
return;
// get weekly database stuff
}
After the DateNavigatorViewModel got instantiated see BELOW
public DateNavigatorViewModel(IMainRepository mainRepo, IMessenger messenger)
{
_mainRepo = mainRepo;
_messenger = messenger;
SelectedDate = DateTime.Now;
// Wether daily/weekly data is fetched the start date for the data is NOW // when the ViewModels are instantiated the first time using a ViewModelLocator...
}
Now the property that got fired setting the DateTime.Now in the Ctor
private DateTime _selectedDate;
public DateTime SelectedDate
{
get { return _selectedDate; }
set
{
if (_selectedDate.Date == value.Date)
return;
_selectedDate = value;
this.RaisePropertyChanged("SelectedDate");
var infoObject = new DateNavigatorInfoObject();
switch (DateNavigatorMode)
{
case DateTest.DateMode.Day:
infoObject.DateNavigatorMode = DateNavigatorMode;
infoObject.SelectedStartDate = value;
break;
case DateTest.DateMode.Week:
infoObject.DateNavigatorMode = DateNavigatorMode;
infoObject.SelectedStartDate = value;
infoObject.SelectedEndDate = value.AddDays(6);
break;
}
_messenger.Send(infoObject);
}
public class DateTest
{
public enum DateMode
{
Day,
Week,
Month,
}
}
The infoObject is send both to the Daily and WeeklyViewModel but depending on the DateNavigatorMode the database fetching is rejected from the ViewModel.
For me thats a solution because it firstly works and second no DAL is involved just the ViewModels.
Someone might mark it as solution if you like it. Critics are welcome too maybe I can still improve something?
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).
EntityFramework 4.3.1.
What is the best way to have calculated properties which should be stored in db, but are not intended to be retrieved from db?
E.g.
[Column("SOME_COLUMN")]
public ulong SomeColumn
{
get { return /*calculate here*/; }
}
I want the value to be persisted when I'm saving to db. But of course - not getting updated when I load from db. I can have an empty setter alongside... But it doesn't seem to work when the column is Key.
Can I have such a Key-column?
Now I'm getting this error:
EntityFramework error: The value of a property that is part of an object's key does not match the corresponding property value stored in the ObjectContext. This can occur if properties that are part of the key return inconsistent or incorrect values or if DetectChanges is not called after changes are made to a property that is part of the key.
Firstly, make the setter private:
[Column("SOME_COLUMN")]
public ulong SomeColumn { get; private set; }
Secondly, add a calculation method in the object (so it can access the private setter):
public void CaculateSomeColumn(){
this.SomeColumn = /* some calculation */;
}
Thirdly, add a SavingChanges callback to the Repository context object (MyEntities). This callback can be defined in the context constructor, and it should set the calculated field for items of your type (MyPoco) that were added or modified:
public MyEntities(){
this.SavingChanges += (sender, eventArgs) => {
ObjectContext context = sender as ObjectContext;
if (context != null)
{
foreach (ObjectStateEntry entry in
context.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(MyPoco)))
{
var myObject = entry.Entity as MyPoco;
myObject.CalculateSomeColumn();
}
}
}
};
}
I am trying to implement a Search as you type functionality ( like the one in the search in the default email app ) - I have a listbox with say 50 items - each item bound to a class object which has string fields ... I wish to search and display items which have the text in the search box in one of their string fields - this as the user keys in to the textbox ... tried a couple of approaches -->
1 >> Using a CollectionViewSource
- Bound a CollectionViewSource to All the items from the DB
- Bound the List Box to a CollectionViewSource
- Setting the filter property of the CollectionViewSource - to whose function which searchs for the text in the Search box in the items and set the e.Accepted - on every keyup event
- Filtering works fine but its slow with 50 items :( - guess cos of filter taking each item and checking if to set e.Accepted property to true
.... One DB call on load but seems to be a lot of processing to decide which element to disply in the filer by the CollectionViewSource
2 >> Filter # DB level
- on keyup - sending the text in the search box to the ViewModel where a function returns an ObservableCollection of objects which has the search string
- ObservableCollection is bound to the listbox
.... Not much processing # the top layer but multiple DB calls on each Keypress - still slow but just a tad faster than Approach One
Is there any other approch you would recommend ? or any suggestions to further optimize the above mentioned approaches? - any tweak to give smooth functioning for the search?
First time into mobile dev :) ... Thanx in advance :)
You can optimize the search process further by delaying the search action by x milliseconds to allow the user to continue the writing of the search criteria. This way, each new typed char will delay the search action by xxx milliseconds till the last char where the action is triggered, you'll get a better performance using just one call instead of say 10 calls. Technically speaking, you can achieve this by using a Timer class.
Here you'll find a sample source code example that shows u how to implement this.
private void txtSearchCriteria_TextChanged(object sender, TextChangedEventArgs e)
{
if (txtSearchCriteria.Text == "Search ...")
return;
if (this.deferredAction == null)
{
this.deferredAction = DeferredAction.Create(() => ApplySearchCriteria());
}
// Defer applying search criteria until time has elapsed.
this.deferredAction.Defer(TimeSpan.FromMilliseconds(250));
}
private DeferredAction deferredAction;
private class DeferredAction
{
public static DeferredAction Create(Action action)
{
if (action == null)
{
throw new ArgumentNullException("action");
}
return new DeferredAction(action);
}
private Timer timer;
private DeferredAction(Action action)
{
this.timer = new Timer(new TimerCallback(delegate
{
Deployment.Current.Dispatcher.BeginInvoke(action);
}));
}
public void Defer(TimeSpan delay)
{
// Fire action when time elapses (with no subsequent calls).
this.timer.Change(delay, TimeSpan.FromMilliseconds(-1));
}
}
public void ApplySearchCriteria()
{
ICollectionView dataView = this.ViewSource.View;
string criteria = this.txtSearchCriteria.Text;
string lowerCriteria = criteria.ToLower();
using (dataView.DeferRefresh())
{
Func<object, bool> filter = word =>
{
bool result = word.ToString().ToLower().StartsWith(lowerCriteria);
return result;
};
dataView.Filter = l => { return filter.Invoke(l); };
}
this.lstWords.ItemsSource = dataView;
if (this.lstWords.Items.Count != 0)
{
this.lblError.Visibility = Visibility.Collapsed;
}
else
{
this.lblError.Visibility = Visibility.Visible;
}
}
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.