After items selection in my ListView other items get randomly selected while scrolling Kotlin - database

I'm new to this. After scrolling my ListView, items in "same position" as items I selected before get auto selected. (Same position I mean position in the screen not in the database.) I had this prob before, because I was selecting items by their indexes of ListView in OnItemClickListener. However i face this prob again although I'm doing it right way I think.
When I click on item in ListView, I get its unique ID and based on that I change SELECTED value of this item (of this row in the database) to 0 or 1 (depending on this if it was clicked or not). After that I switch backgrnd color to grey (or back to white). This is handled in CursorAdapter which distinguishes SELECTED property.
Here's my code.
OnCreate in MainActivity.kt
val dbHelper = DBHelper(this)
val db = dbHelper.writableDatabase
val myCursor = db.rawQuery("SELECT * FROM ${ContractClass.FeedReaderContract.TABLE_NAME}", null)
val myAdapter = CursorAdapter(this, myCursor)
myListView.adapter = myAdapter
myListView.setOnItemClickListener { _, view, _, _ ->
val text = view.txtName.text
val select = "${ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD} MATCH ?"
val selectCursor = db.query(
ContractClass.FeedReaderContract.TABLE_NAME, // The table to query
null, // The array of columns to return (pass null to get all)
select, // The columns for the WHERE clause
arrayOf("$text"), // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
null // The sort order
)
with(selectCursor) {
while (moveToNext()) {
val itemSel = getInt(getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED))
if (itemSel == 1){
val values = ContentValues().apply {
put(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED, 0)
}
val count = db.update(
ContractClass.FeedReaderContract.TABLE_NAME, values, select, arrayOf("$text"))
}else{
val values = ContentValues().apply {
put(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED, 1)
}
val count = db.update(
ContractClass.FeedReaderContract.TABLE_NAME, values, select, arrayOf("$text"))
}
}
}
}
CursorAdapter.kt
class CursorAdapter(context: Context, cursor: Cursor) : CursorAdapter(context, cursor, 0) {
// The newView method is used to inflate a new view and return it,
// you don't bind any data to the view at this point.
override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
if (cursor.getInt(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)) == 0){
return LayoutInflater.from(context).inflate(R.layout.row_list_row, parent, false)
}else{
return LayoutInflater.from(context).inflate(R.layout.user_list_row_selected, parent, false)
}
}
// The bindView method is used to bind all data to a given view
// such as setting the text on a TextView.
override fun bindView(view: View, context: Context, cursor: Cursor) {
// Find fields to populate in inflated template
val tvBody = view.findViewById<View>(R.id.txtName) as TextView
val tvPriority = view.findViewById<View>(R.id.txtComment) as TextView
val tvPriority2 = view.findViewById<View>(R.id.txtThird) as TextView
val tvPriority3 = view.findViewById<View>(R.id.txtThi) as TextView
// Extract properties from cursor
val body = cursor.getString(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD))
val priority = cursor.getString(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_DEFN))
val priority2 = cursor.getInt(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract._id))
val priority3 = cursor.getInt(cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED))
// Populate fields with extracted properties
tvBody.text = body
tvPriority.text = priority.toString()
tvPriority2.text = priority2.toString()
tvPriority3.text = priority3.toString()
}
}
Database Table Create
private val SQL_CREATE_ENTRIES =
"CREATE VIRTUAL TABLE ${ContractClass.FeedReaderContract.TABLE_NAME} USING fts4(" +
"${ContractClass.FeedReaderContract._id}," +
"${ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD} TEXT," +
"${ContractClass.FeedReaderContract.COLUMN_NAME_DEFN} TEXT," +
"${ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED} INTEGER)"
I found similar post here: List view with simple cursor adapter items checked are un-checked during scroll
but I think, they suggest what I've already done.
Thanks for any help.

You don't appear to be changing the ListView's Cursor after you have made changes to/updated the underlying data.
Try, after making changes to the underlying data, updating the ListView's Cursor by
re-querying the data and then
using the Adapter's swapCursor(myCursor) (or the notiftDatasetChanged() method)
Here's an equivalent of your App but in Java rather than Kotlin (not having any luck converting as I hardly ever use Kotlin).
This I believe does what you want. That is,
If you select an unselected row then all rows that contain the enword are selected and greyed out and the selected column value is set to 1.
If you select a selected (grey) row then those rows that contain the enword are de-selected and are change to white with the selected column value being changed back to 0
Note that rather than create an FTS table I've mimicked the FTS and used LIKE instead of MATCH.
Are toggled if they are selected the background is grey else white. e.g. initially it is :-
If cat is clicked (2nd row) then all other cat rows are also toggled and greyed as per :-
and so on.
The Code
MainActivity (the file I had issues converting)
public class MainActivity extends AppCompatActivity {
DBHelper dbhelper;
ListView myListView;
MyCursorAdapter myAdapter;
Cursor mCursor;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myListView = this.findViewById(R.id.myListView);
dbhelper = new DBHelper(this);
addSomeTestingData();
manageListView();
}
private void manageListView() {
mCursor = dbhelper.getAllRows();
if (myAdapter == null) {
myAdapter = new MyCursorAdapter(this,mCursor);
myListView.setAdapter(myAdapter);
myListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
dbhelper.updateSelected(mCursor.getString(mCursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD)));
manageListView();
}
});
} else {
myAdapter.swapCursor(mCursor);
}
}
private void addSomeTestingData() {
if (DatabaseUtils.queryNumEntries(dbhelper.getWritableDatabase(),ContractClass.FeedReaderContract.TABLENAME) > 1) return;
for (int i=0; i < 10; i++) {
dbhelper.addRow("Apple", "Thing that falls from trees");
dbhelper.addRow("Cat", "Something that is furry and sits on mats");
dbhelper.addRow("Bear", "Something that is furry that eats honey but doesn't ssit on a mat");
dbhelper.addRow("Dog", "Something is furry and friendly");
dbhelper.addRow("Echida", "An upside down hedgehog");
dbhelper.addRow("Ferret", "Something that is furry and found up trouser legs");
dbhelper.addRow("Giraffe", "Something that has 5 legs one pointing up");
dbhelper.addRow("Hippo", "An animal that loves mud and water but not humans");
dbhelper.addRow("Ibis", "A white feathered flying thing");
dbhelper.addRow("Jaguar", "A car or a large black cat");
dbhelper.addRow("Kangaroo", "A marsupial that boxes, skips and has a puch for shopping trips");
dbhelper.addRow("Lizard", "A rock dweller");
dbhelper.addRow("Mammoth", "A big hairy elephant now extinct");
dbhelper.addRow("Nuthatch", "A small bird that does not customise nuts so they have hatches.");
dbhelper.addRow("Ostrich", "A l argefast running bird that does not fly");
dbhelper.addRow("Panther", "A skinny pink cat that walks on only two of it's four lehs");
dbhelper.addRow("Queen", "A female rule of a country");
dbhelper.addRow("Rhinocerous", "A Hippo like animal that has a name that is hard to spell");
dbhelper.addRow("Tiger", "A live verion of Winnie the Pooh's friend Tigger");
dbhelper.addRow("Stork", "A skinny ostrich that flies and delivers children through Dream World.");
}
}
}
Obviously the addSomeTestingData method is just for that.
Note that there's hardly any other code. The DB access has all been moved to the DBHelper class.
The crux of the matter is the manageListView method.
First the Cursor used by the adapter is populated.
The Adapter hasn't been instantiated and is therefore null is instantiated and then tied to the ListView.
The OnItemClickListener is added noting that it calls the manageListView method after the database has been updated.
If the Adapter has been instantiated then the swapCursor method is called that tells the adapter that underlying cursor has been changed.
DBHelper.java
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(#Nullable Context context) {
super(context, ContractClass.DBNAME,null,ContractClass.DBVERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(ContractClass.FeedReaderContract.CRTSQL);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public Cursor getAllRows() {
SQLiteDatabase db = this.getWritableDatabase();
return db.query(ContractClass.FeedReaderContract.TABLENAME,null,null,null,null,null,null);
}
public void updateSelected(String selected) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("UPDATE "
+ ContractClass.FeedReaderContract.TABLENAME
+ " SET " + ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED
+ "= CASE " + ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED +
" WHEN 1 THEN 0 ELSE 1 END " +
" WHERE " + ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD + " LIKE ?",
new String[]{selected}
);
}
public long addRow(String enWord, String definition) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD,enWord);
cv.put(ContractClass.FeedReaderContract.COLUMN_NAME_DEFN,definition);
return db.insert(ContractClass.FeedReaderContract.TABLENAME,null,cv);
}
}
Selecting all rows has been moved here as the getAllRows method
The addRow is just to allow some testing data to be added.
The updateSelected method, rather than extracting rows into a Cursor, to drive the updates instead uses a CASE WHEN ELSE END clause to toggle the value and should be more efficient.
Note that instead of MATCH as MATCH is FTS specififc (I believe) LIKE has been used as the underlying table is not an FTS table. You would use MATCH
MyCursorAdapter.java
public class MyCursorAdapter extends CursorAdapter {
public MyCursorAdapter(Context context, Cursor c) {
super(context, c, false);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.row_list_row,parent,false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
if (cursor.getInt(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)) < 1) {
view.setBackgroundColor(0xFFF9F9F9);
} else {
view.setBackgroundColor(0xFFD9D9D9);
}
TextView tvBody = view.findViewById(R.id.txtName);
TextView tvPriority = view.findViewById(R.id.txtComment);
TextView tvPriority2 = view.findViewById(R.id.txtThird);
TextView tvPriority3 = view.findViewById(R.id.txtThi);
tvBody.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_ENWORD)));
tvPriority.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_DEFN)));
tvPriority2.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract._id)));
tvPriority3.setText(cursor.getString(cursor.getColumnIndex(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)));
}
}
The main difference is that rather than juggle swapping layouts the background of the view is changed in the bindView method, rather than the newView method.

You need to override getItemViewType(position) if you want to use multiple different view types. If you don't, then the adapter has no way to know whether or not the View instance it is passing as convertView is of the right type, and you will wind up with badly-recycled views.
This doesn't appear to be trivial for CursorAdapter (with which I don't have any experience). I think the right way to do it should be something like this:
override fun getItemViewType(position: Int): Int {
cursor.moveToPosition(position)
val columnIndex = cursor.getColumnIndexOrThrow(ContractClass.FeedReaderContract.COLUMN_NAME_SELECTED)
return when (cursor.getInt(columnIndex)) {
0 -> 0
else -> 1
}
}
I think also that you should change newView() to leverage these types. Leaving what you have should work, but it would be duplicated code.
override fun newView(context: Context, cursor: Cursor, parent: ViewGroup): View {
val layoutId = when (val type = getItemViewType(cursor.position)) {
0 -> R.layout.row_list_row
1 -> R.layout.user_list_row_selected
else -> throw IllegalStateException("unexpected viewType: $type")
}
return LayoutInflater.from(context).inflate(layoutId, parent, false)
}

Related

How to report a value in real time in Fink?

I want three values, they are aggValueInLastHour aggValueInLastDay aggValueInLastThreeDay.
I've tried like below.
But I don't want to wait, means that I'm not prefer to use sliding window to do aggregation.(3 day window must wait three days' data, this is unbearable for our system.)
How can I get last 3 day aggregation value when first event come?
Thanks for any advice in advance!
If you want to get more frequent updates you can use QueryableState, polling the state at a rate that suits your use case.
You can make use of the ContinuousEventTimeTrigger, which will cause your window to fire on a shorter time period than the the full window, allowing you to see the intermediate state. You can optionally wrap that in a PurgingTrigger if the downstream consumers of your sink are expecting each output to be a partial aggregation (rather than the full current state) and sums them up.
I've tried CEP.
code:
AfterMatchSkipStrategy strategy = AfterMatchSkipStrategy.skipShortOnes();
Pattern<RiskEvent, ?> loginPattern = Pattern.<RiskEvent>begin("start", strategy)
.where(eventTypeCondition)
.timesOrMore(1)
.greedy()
.within(Time.hours(1));
KeyedStream<RiskEvent, String> keyedStream = dataStream.keyBy(new KeySelector<RiskEvent, String>() {
#Override
public String getKey(RiskEvent riskEvent) throws Exception {
// key by user for aggregation
return riskEvent.getEventType() + riskEvent.getDeviceFp();
}
});
PatternStream<RiskEvent> eventPatternStream = CEP.pattern(keyedStream, loginPattern);
eventPatternStream.select(new PatternSelectFunction<RiskEvent, RiskResult>() {
#Override
public RiskResult select(Map<String, List<RiskEvent>> map) throws Exception {
List<RiskEvent> list = map.get("start");
ArrayList<Long> times = new ArrayList<>();
for (RiskEvent riskEvent : list) {
times.add(riskEvent.getEventTime());
}
Long min = Collections.min(times);
Long max = Collections.max(times);
Set<String> accountList = list.stream().map(RiskEvent::getUserName).collect(Collectors.toSet());
logger.info("时间范围:" + new Date(min) + " --- " + new Date(max) + " 事件:" + list.get(0).getEventType() + ", 设备指纹:" + list.get(0).getDeviceFp() + ", 关联账户:" + accountList.toString());
return null;
}
});
maybe you notice that, the skip strategy skipShortOnes is a customized strategy.
Show you my modification in CEP lib.
add strategy in Enum.
public enum SkipStrategy{
NO_SKIP,
SKIP_PAST_LAST_EVENT,
SKIP_TO_FIRST,
SKIP_TO_LAST,
SKIP_SHORT_ONES
}
add access method in AfterMatchSkipStrategy.java
public static AfterMatchSkipStrategy skipShortOnes() {
return new AfterMatchSkipStrategy(SkipStrategy.SKIP_SHORT_ONES);
}
add strategy actions in discardComputationStatesAccordingToStrategy method at NFA.java.
case SKIP_SHORT_ONES:
int i = 0;
List>> tempResult = new ArrayList<>(matchedResult);
for (Map> resultMap : tempResult) {
if (i++ == 0) {
continue;
}
matchedResult.remove(resultMap);
}
break;

Entity Framework : Create a model from Dictionary<TKey,TValue> to be mapped to a database table

Earlier I had a table named ApplicationConfiguration which simply had [Key],[Value] columns to store some config data. This was queried straight away using SQL queries.
Now I intend to make use of Entity Framework (EF) Code First approach to query this table. The specialty of this table is that the table will have only a fixed number of rows in its lifetime. Only the Value column can be updated.
So as per the code first approach, we have to first write our POCO classes with its properties that will be mapped to columns in the underlying table. However, I wish to have a Dictionary<> structure to represent these configuration KV pairs. My concern is, will EF be able to fire update queries against any updation to the the value of a particular pair.
Also since I am using Code First approach, I would want some seed data(i.e the fixed number of rows and its initial content) to the added after the table itself is created on the fly when the application is first executed.
If Dictionary<> cannot be used, please suggest some alternative. Thanks in advance.
Coded this way:
public class ApplicationConfiguration
{
public int Id { get; set; }
public string Key { get; set; }
public int Value { get; set; } // should be string, but I'm lazy
}
class Context : DbContext
{
internal class ContextInitializer : DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
var defaults = new List<ApplicationConfiguration>
{
new ApplicationConfiguration {Key = "Top", Value = 5},
new ApplicationConfiguration {Key = "Bottom", Value = 7},
new ApplicationConfiguration {Key = "Left", Value = 1},
new ApplicationConfiguration {Key = "Right", Value = 3}
};
// foreach (var c in defaults)
// context.ConfigurationMap.Add(c.Key, c); // by design, no IReadOnlyDictionary.Add
foreach (var c in defaults)
context.ApplicationConfigurations.Add(c);
base.Seed(context);
}
}
public Context()
{
Database.SetInitializer(new ContextInitializer());
}
private IDbSet<ApplicationConfiguration> ApplicationConfigurations
{
get { return Set<ApplicationConfiguration>(); }
}
public IReadOnlyDictionary<string, ApplicationConfiguration> ConfigurationMap
{
get { return ApplicationConfigurations.ToDictionary(kvp => kvp.Key, kvp => kvp); }
}
}
Used this way:
using (var context = new Context())
{
ReadConfigurationOnly(context.ConfigurationMap);
}
using (var context = new Context())
{
ModifyConfiguration(context.ConfigurationMap);
context.SaveChanges();
}
static void ReadConfigurationOnly(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
foreach (var k in configuration.Keys)
Console.WriteLine("{0} = {1}", k, configuration[k].Value);
}
static void ModifyConfiguration(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
foreach (var k in configuration.Keys)
configuration[k].Value++; // this is why I was lazy, using an int for a string
}
So, I wrote it up this way — using an int Value property rather than a string — just so I could run the "Used this way" code over and over, and see the database update each time, without having to come up with some other way to change Value in an interesting way.
It's not quite as nifty here to use a IReadOnlyDictionary<string, ApplicatonConfiguration> instead of a IReadOnlyDictionary<string, string>, the way we'd really like, but that's more than made up for by the fact that we can easily modify our collection values without resorting to a clumsier Set method taking a dictionary as input. The drawback, of course, is that we have to settle for configuration[key].Value = "new value" rather than configuration[key] = "new value", but — as I say — I think it's worth it.
EDIT
Dang! I wrote this code up specifically to answer this question, but I think I like it so much, I'm going to add it to my bag of tricks ... this would fit in really well when my company goes from local databases to Azure instances in the cloud, and the current app.config has to go into the database.
Now all I need is a ContextInitializer taking a System.Configuration.ConfigurationManager as a ctor parameter in order to seed a new database from an existing app.config ...
I don't think you can map a table directly to a Dictionary; you will probably have to write your own wrapper to fill a dictionary from the table and update it back to the DB. Entities are each a row of a given table... Something like this (untested):
public Dictionary<string, string> GetDictionary()
{
Dictionary<string, string> dic = new Dictionary<string, string>();
using (var db = new Context())
{
var configs = db.ApplicationConfiguration.Select();
foreach (var entry in configs)
{
dic.Add(config.Key, config.Value);
}
}
return dic;
}
public void SaveConfig(Dictionary<string, string> dic)
{
using (var db = new Context())
{
foreach (KeyValuePair kvp in dic)
{
if (!db.ApplicationConfiguration.First(a => a.Key == kvp.Key).Value == kvp.Value)
{
var ac = new ApplicationConfiguration();
ac.Key = kvp.Key;
ac.Value = kvp.Value;
db.Entry(ac).State = EntityState.Modified;
}
}
db.SaveChanges();
}
}
For your second question, you want to use the Seed() method to add initial values to the database. See here for an example implementation.

Dynamically add check box and records from database in javaFX 2.2 Table View

I am writing a client-server java FX application with a table View. I have a database in the server side and I want from the Client to load the table Columns and table Records from the db to a table View dynamically. So far I have found many hints, on how to do this successfully. The thing is that I want to add to the table a column 'select' which is a check box. Below is my code.
private void AddToTableRecordsFromDB(TabPane tp){
tableview = (TableView) tp.lookup("#table");
ObservableList<Object> data = null;
try {
String[] columnNames = (String[]) Login.Login.in.readObject();
ArrayList<ArrayList> al = (ArrayList<ArrayList>) Login.Login.in.readObject();
/**********************************
* TABLE COLUMN ADDED DYNAMICALLY *
**********************************/
TableColumn select = new TableColumn("Select");
select.setCellValueFactory(new PropertyValueFactory("invited"));
select.setCellFactory(new Callback<TableColumn<ObservableValue, Boolean>, TableCell<ObservableValue, Boolean>>() {
public TableCell<ObservableValue, Boolean> call(TableColumn<ObservableValue, Boolean> p) {
return new CheckBoxTableCell<ObservableValue, Boolean>();
}
});
tableview.getColumns().add(select);
for(int i=0 ; i<columnNames.length; i++){
//use non property style for making dynamic table
final int j = i;
TableColumn col;
col = new TableColumn(columnNames[i]);
col.setCellValueFactory(new Callback<CellDataFeatures<ObservableList,String>,ObservableValue<String>>(){
#Override
public ObservableValue<String> call(CellDataFeatures<ObservableList, String> param) {
return new SimpleStringProperty(param.getValue().get(j).toString());
}
});
tableview.getColumns().add(col);
}
/********************************
* Data added to ObservableList *
********************************/
data = FXCollections.observableArrayList();
for(int i=0 ; i<al.size(); i++){
ObservableList<ArrayList> row = FXCollections.observableArrayList(al.get(i));
data.add(row);
}
//FINALLY ADDED TO TableView
tableview.setItems(data);
} catch (IOException | ClassNotFoundException ex) {
Logger.getLogger(Developer_Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
I took the CheckBoxTableCell class from the JavaFX2.0 Ensemble.
The database loads successfully in to the table view and also my 'select' column is created, but I can not see any check boxes in the rows.
Any help please ?
You say you are using "non property style" to add columns dynamically, and your table view made of list of list items. I guess there is no getInvited() method in those data structure model. However by setting select.setCellValueFactory(new PropertyValueFactory("invited")); the table column will look for that method. Set cell value factory with valid value.
EDIT: I didn't test but can you try the code below.
select.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<ObservableList, String>, ObservableValue<String>>() {
public ObservableValue<String> call(TableColumn.CellDataFeatures<ObservableList, String> p) {
return new SimpleStringProperty(p.getValue().get(0).toString());
}
});
Note the get(0). Namely it would be better if readObject() returns at least 1 item. Another note is CheckBoxTableCell needs a Callback which returns ObservableProperty<Boolean> and binds bidirectionally so I think it is better to implement your own cell factory containing checkbox, regarding to your data model.

Windows Form Combobox.Items.Clear() leaves empty slots

I am working on a Windows Form that has multiple comboboxes. Depending on what is chosen in the first combobox determines what items are filled into the second combobox. The issue I am running into is if I choose ChoiceA in ComboBox1, ComboBox2 is clear()ed, then is filled with ChoiceX, ChoiceY, and ChoiceZ. I then choose ChoiceB in ComboBox1, ComboBox2 is clear()ed, but there are no choices to add to ComboBox2, so it should remain empty. The issue is, after choosing ChoiceB, there's a big white box with three empty slots in ComboBox2. So, basically, however many items are cleared N, that's how many empty slots show up after choosing ChoiceB.
This might be a tad confusing, I hope I explained it well enough.
-- EDIT Adding Code, hope it helps clear things up. BTW, mainItemInfo is another "viewmodel" type class. It interfaces back into the form to make updates.
private void cmbType_SelectedIndexChanged(object sender, EventArgs e)
{
DropDownItem item = (DropDownItem)cmbType.SelectedItem;
if (!String.IsNullOrWhiteSpace(item.Text))
{
cmbBrand.Enabled = true;
btnAddBrand.Enabled = true;
mainItemInfo.FillBrands(new Dictionary<string, string> { { "Type", item.Text } });
mainItemInfo.SyncBrands(this);
}
}
public void FillBrands(Dictionary<string, string> columnsWhere)
{
// Clear list
Brands.Clear();
// Get data
StorageData storage = new StorageData(File.ReadAllLines(ItemsFilePath));
// Fill Brands
foreach (string type in storage.GetDistinctWhere(columnsWhere, "Brand"))
{
Brands.Add(type, new DropDownItem(type, type));
}
}
public void SyncBrands(IPopupItemInfo form)
{
form.ClearcmbBrand();
var brands = from brand in Brands.Keys
orderby Brands[brand].Text ascending
select brand;
foreach (var brand in brands)
{
form.AddTocmbBrand(Brands[brand]);
}
}
public void AddTocmbBrand(DropDownItem brand)
{
cmbBrand.Items.Add(brand);
}
public void ClearcmbBrand()
{
cmbBrand.Items.Clear();
}
Simply, you can add an item then clear the combobox again:
cmbBrand.Items.Clear();
cmbBrand.Items.Add(DBNull.Value);
cmbBrand.Items.Clear();
You should able to set the datasource of listbox2 to null to clear it, then set it again with the new data.
So, in pseudo-code, something like:
ItemSelectedInListBox1()
{
List futureListbox2Items = LoadOptionsBaseOnSelectedItem(item)
Listbox2.Datasource = null
Listbox2.Datasource = futureListBox2Items
}
That should refresh the list of items displayed in Listbox2 with no white spaces.
I was able to fix the extra space. I changed the Add and Clear methods to:
public void AddTocmbModel(DropDownItem model)
{
cmbModel.Items.Add(model);
cmbModel.DropDownHeight = cmbModel.ItemHeight * (cmbModel.Items.Count + 1);
}
public void ClearcmbModel()
{
cmbModel.Items.Clear();
cmbModel.DropDownHeight = cmbModel.ItemHeight;
}

How to find matching words in Text Box and Datagrid when VirtualizingStackPanel.IsVirtualizing="true"?

I have Datagrid and Text Box in my Form. Datagrid is showing me existing items in my stock. I use Text Box to search and set focus to that row which is matching with my Text Box. Now it is working fine when VirtualizingStackPanel.IsVirtualizing="false" but it is very slow and getting a lot RAM resource.
Here is my code for this.
public IEnumerable<Microsoft.Windows.Controls.DataGridRow> GetDataGridRows(Microsoft.Windows.Controls.DataGrid grid)
{
var itemsSource = grid.ItemsSource as IEnumerable;
if (null == itemsSource) yield return null;
foreach (var item in itemsSource)
{
var row = grid.ItemContainerGenerator.ContainerFromItem(item) as Microsoft.Windows.Controls.DataGridRow;
if (null != row) yield return row;
}
}
private void SearchBoxDataGrid_TextChanged(object sender, TextChangedEventArgs e)
{
var row = GetDataGridRows(AssortDataGrid);
/// go through each row in the datagrid
foreach (Microsoft.Windows.Controls.DataGridRow r in row)
{
DataRowView rv = (DataRowView)r.Item;
// Get the state of what's in column 1 of the current row (in my case a string)
string t = rv.Row["Ассортимент"].ToString().ToLower();
if (t.StartsWith(SearchBoxDataGrid.Text.ToLower()))
{
AssortDataGrid.SelectedIndex = r.GetIndex();
AssortDataGrid.ScrollIntoView(AssortDataGrid.SelectedItem);
break;
}
}
}
What I want is to make it VirtualizingStackPanel.IsVirtualizing="true" but in this case my method is not working. I know why it is not working, my code will work only for showing part of Datagrid.
What do you recommend? How to fix this issue? Any idea will be appreciated. If you give any working code it will be fantastic. I hope I could explain my problem.
Virtualization means that WPF will reuse the UI components, and simply replace the DataContext behind the components.
For example, if your Grid has 1000 items and only 10 are visible, it will only render around 14 UI items (extra items for scroll buffer), and scrolling simply replaces the DataContext behind these UI items instead of creating new UI elements for every item. If you didn't use Virtualization, it would create all 1000 UI items.
For your Search to work with Virutalization, you need to loop through the DataContext (DataGrid.Items) instead of through the UI components. This can either be done in the code-behind, or if you're using MVVM you can handle the SeachCommand in your ViewModel.
I did a little coding and make it work. If anyone needs it in future please, use it.
Firstly I am creating List of Products
List<string> ProductList;
Then on Load Method I list all my products to my Product List.
SqlCommand commProc2 = new SqlCommand("SELECT dbo.fGetProductNameFromId(ProductID) as ProductName from Assortment order by ProductName desc", MainWindow.conn);
string str2;
SqlDataReader dr2 = commProc2.ExecuteReader();
ProductList = new List<string>();
try
{
if (dr2.HasRows)
{
while (dr2.Read())
{
str2 = (string)dr2["ProductName"];
ProductList.Insert(0, str2.ToLower ());
}
}
}
catch (Exception ex)
{
MessageBox.Show("An error occured while trying to fetch data\n" + ex.Message);
}
dr2.Close();
dr2.Dispose();
After that I did some changes in SearchBoxDataGrid_TextChanged
private void SearchBoxDataGrid_TextChanged(object sender, TextChangedEventArgs e)
{
int pos = 0;
string typedString = SearchBoxDataGrid.Text.ToLower();
foreach (string item in ProductList)
{
if (!string.IsNullOrEmpty(SearchBoxDataGrid.Text))
{
if (item.StartsWith(typedString))
{
pos = ProductList.IndexOf(item);
AssortDataGrid.SelectedIndex = pos;
AssortDataGrid.ScrollIntoView(AssortDataGrid.SelectedItem);
break;
}
}
}
}
Now it works when VirtualizingStackPanel.IsVirtualizing="true".
That is all.

Resources