I am not understanding the following 4 points about databinding. Not sure if i get unexpected results (outdated data in code behind), or if this is just a result of me misunderstanding how things work.
Bound data to a textbox updates when i leave the textbox. What event
EXACTLY does this? LostFocus?
When using the now changed data in code behind
it seems to still use the OLD data. Why is this happening? Could
point 3 be the reason?
After the textbox updates i did a test and set datacontext
to nothing and reaplied datacontext to the same scource. The values
shown are the values before i edited them. Why did they show up after editing, but
returned to the old values after rebinding?
After changing the values for the second time it seems
like code behind uses the data after my first change. Rebinding like
in point 3 leads to the same result (value after first change,
second change ignored). Seems like code behind is always one update behind, can i change this?
Anyone able to explain why this happens?
Desired behavior:
I want the people count to update when I edit the housing count. Preferable on the fly, but after losing focus is fine. When losing focus the value for isle ID 0 should be the right one tho, and not the outdated value.
For easier understanding, a picture with 3 screens and related code samples.
http://www.mathematik-lehramtsstudium.de/BindingExample.jpg
My class:
//class for isles
public class isle : INotifyPropertyChanged
{
//Dummyvariables,...
private int _intBauer;
private int _intBauerBev;
//variables
public int intIsleID { set; get; } //isle ID
public string strName { set; get; } //isle name
public int intBauer //housing count
{
set
{
this._intBauer = value;
NotifyPropertyChanged("intBauer"); NotifyPropertyChanged("intBauerBev");
}
get
{
return _intBauer;
}
}
public int intBauerBev //each house hosts 8 people
{
set { this._intBauerBev = value;}
get { return intBauer * 8; }
}
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
LostFocus-Event for updating the right side oft he page
private void textboxHäuser_LostFocus(object sender, RoutedEventArgs e)
{
//Gesamtzahl neu berechnen
listIsles[0].intBauer = 0;
for (int i = 1; i < 10; i++)
{
listIsles[0].intBauer += listIsles[i].intBauer;
}
//hard refresh DataContext, since it does not seem to update itself
//leaving these two lines out of my code changes nothing tho, as expected
gridInfoGesamt.DataContext = "";
gridInfoGesamt.DataContext = listIsles[0];
}
The issue i was facing is the order in which events get fired in this case. Or more accurate: Two things happening at once.
TextBox uses the "LostFocus"-Event to update the property, same as the event i used to update my other TextBox controls. Since both fired at once i used the "outdated" data for calculations, therefore it looked like my UI lagged one step behind on one side.
To fix this i simply had to change the way my TextBox updates the property, by doing my binding like this in XAML:
Text="{Binding intBauer, UpdateSourceTrigger=PropertyChanged}"
Now the property is updated instantly, before "LostFocus" and even before "TextChanged".
This also opens the possibility to update the UI as the user changes values and not only after he is finished. Much cleaner and better looking.
Related
I am not quite sure if I am asking the right question. I assume other people have had this issue.
I built my own Blazor Grid component. I am using an bound to a property.
I have a function to load my grid. I changed my bound property to a full getter,setter. In the setter, I call my function to load the grid. This works fast and easy in pretty much all instances. But, I have one grid that when binding it will take a few extra seconds to complete.
The problem: I can't seem to figure out how to get my waiting spinner component to show when loading my grid.
Example Blazor Markup:
#if (dataGrid == null)
{
<hr />
<BitcoSpinner></BitcoSpinner>
}
else
{
<BitcoGrid TheGrid="dataGrid"></BitcoGrid>
}
Here is my property and GridLoading:
private string selectedGroup1 = "";
public string selectedGroup
{
get => selectedGroup1;
set
{
selectedGroup1 = value;
LoadGrid();
}
}
private void LoadGrid()
{
dataGrid = null;
PT_Grid_Admin ptGrid = new PT_Grid_Admin(permitTraxLibrary, gridParams);
dataGrid = ptGrid.ADMIN_FeeList(feeList.Fee_Key, selectedGroup);
}
You should define LoadGrid method asynchronously. Therefore, at the beginning of the program, when the data grid value is set, your spinner will be displayed until the data grid value is not received. Then, after receiving the data grid value, the else part of the condition will be executed and its value will be displayed to the user.
It may not take much time to receive information from the DB in local mode, so the following code can be used to simulate the delay:
System.Threading.Thread.Sleep(5000);
In general, I think that if your code changes like this, you can see the spinner.
private string selectedGroup1 = "";
public string selectedGroup
{
get => selectedGroup1;
set
{
selectedGroup1 = value;
LoadGrid();
}
}
private async Task LoadGrid()
{
dataGrid = null;
System.Threading.Thread.Sleep(5000);
.
.
}
Of course, it is better to load the datagrid in OnInitializedAsync method. For more info you can refer to this link.
I'm trying to create a simple listbox with ObjectListView (WinForm, C#). The goal is to have a single value (a double) and a check box.
I want to be able to edit the double value by Single Click, so here are the relevant lines of code from my MyWindow.Designer.cs file (i've left out the default values for efficiency):
this.olvDepths = new BrightIdeasSoftware.ObjectListView();
this.olvColumn1 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
...
this.olvDepths.CellEditActivation = BrightIdeasSoftware.ObjectListView.CellEditActivateMode.SingleClick;
this.olvDepths.CheckBoxes = true;
this.olvDepths.CheckedAspectName = "IsDefault";
this.olvDepths.FullRowSelect = true;
//
// olvColumn1
//
this.olvColumn1.AspectName = "Depth";
this.olvColumn1.Text = "";
this.olvColumn1.IsEditable = true;
I then create a list of my class (ShieldingEntry) and use the olvDepths.SetObjects() with the list. My ShieldingEntry class looks like this:
public class ShieldingEntry
{
public double Depth { get; set; }
public bool IsDefault { get; set; }
}
However, when I click the field, it doesn't go into edit mode. I've also tried the DoubleClick, SingleClickAlways, and F2Only modes and they don't work either.
The Checkbox works fine.
************** I have additional information *********************
I've pulled and build the ObjectListView source, so I could step through it.
I put a breakpoint in the OLV StartCellEdit method and it gets called and appears to setup and select the control appropriately. It just never appears...
As I noted in the comments on the answer below, I've got this control on a tabbed dialog, and if I switch to another tab, then back, the control works fine.
What am I missing?
I've used ObjectListView before, and here is what I had to do:
Handle the CellEditStarting event. This event is raised when the cell goes into edit mode. Since OLV doesn't really have built-in editors, you have to make your own. Then handle the CellEditFinishing event to validate the data before putting it back into your model.
So first, handling the CellEditStarting event:
private void objlv_CellEditStarting(object sender, CellEditEventArgs e)
{
//e.Column.AspectName gives the model column name of the editing column
if (e.Column.AspectName == "DoubleValue")
{
NumericUpDown nud = new NumericUpDown();
nud.MinValue = 0.0;
nud.MaxValue = 1000.0;
nud.Value = (double)e.Value;
e.Control = nud;
}
}
This creates your editing control. If you want to make sure the size is right, you can set the size of the control (in this case a NumericUpDown) to the cell bounds using e.CellBounds from the event object.
This will show the editor when you click in the cell. Then you can handle the editor finished event to validate the data:
private void objlv_CellEditFinishing(object sender, CellEditEventArgs e)
{
if (e.Column.AspectName == "DoubleValue")
{
//Here you can verify data, if the data is wrong, call
if ((double)e.NewValue > 10000.0)
e.Cancel = true;
}
}
I don't think handling it is required, but its good practice to validate data from the user.
The editing control in the CellEditStarting event can be any control, even a user defined one. I've used a lot of user defined controls (like textboxes with browse buttons) in the cell editor.
[Edit]
I uploaded an example here dropbox link that seems to work. Might not be in the exact view as needed, but seems to do the job.
For anyone else with this problem. I had it specifically when trying to edit a 'null' value in a decimal? on the OLV on a tab page. Solution for me was to set UseCustomSelectionColors to 'False'. I didn't look elsewhere to see if it was reported as a bug. Seems like a bug.
I'm using wpf, mvvmlight and EF. I have two properties on my entity object which are loosely linked and in my XAML, I would like to have one change when the other changes. Right now, I'm having a problem with the first property changed event firing twice.
My xaml setup is like this, I have DataGrid up top on my xaml, I have my
ItemsSource="{Binding MonthlyDonorDetails}" IsSynchronizedWithCurrentItem="True"
The textbox is like,
<TextBox Grid.Column="0" Text="{Binding BankCustomerID}"/>
Next, in my VM, I load up the data like this,
int rowID = 1;
foreach(var row in monthlyDonorsQuery)
{
row.RowID = rowID++;
row.PropertyChanged += new PropertyChangedEventHandler(MonthlyDonorDataRow_PropertyChanged);
}
MonthlyDonorDetails = new ObservableCollection<MonthlyDonorFundCode>(monthlyDonorsQuery);
This affords me a row id on each row to help user differentiate, and (I'm thinking) allows me to setup a handler for when individual fields change within the row. Then I set the backing collection for the grid. And this is all working just fine.
NOW, I want to be able to keep two fields in lock-step with each other, an AccountID and an CustomerID. When the user manually\typing\input changes the AccountID fields, I want to have some code to change the CustomerID, but I don't want this firing needlessly multiple times. Right now its firing twice, and I don't know why? Can anyone see my mistake please?
private void MonthlyDonorDataRow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "FirstName" || e.PropertyName == "LastName")
RaisePropertyChanged("DonorName");
if(e.PropertyName == "AccountID")
{
MonthlyDonorFundCode monthlyDonation = sender as MonthlyDonorFundCode;
int customerID = GetCimsIdFromBankCustomerID(monthlyDonation.AccountID);
Debug.WriteLine("should be setting to " + customerID);
}
}
When I'm debugging this, all I can see is the EF setter is getting called twice, but I don't know why cause I'm not setting that value. Any help\guidance would be appreciated. Thank you very much.
I think I found out my problem. But it raises another problem for me. I'm calling the method which adds a PropertyChanged handler, twice, while loading. That's another problem I have to figure out.
Thank you Lavr for trying to help me out.
Put breakpoint in AccountID setter and look at stack trace then setter is called. You can find "who" updating your property twice.
Also you can update AccountID setter like this:
private int _accountId;
public int AccountID
{
get { return this._accountId; }
set
{
if (this._accountId == value) return;
this._accountId = value;
this.RisePropertyChanged("AccountID");
}
}
I have the same problem with twice invocation of propertychanged. The same thing when I try to catch the extra invoke of the propertychanged, the call stack says it is called from the [External Code]. After I upgraded my Xamarin version from 4.0 to latest (Xamarin 5.0).
I am using a ListView control to display some lines of data. There is a background task which receives external updates to the content of the list. The newly received data may contain less, more or the same number of items and also the items itself may have changed.
The ListView.ItemsSource is bound to an OberservableCollection (_itemList) so that changes to _itemList should be visible also in the ListView.
_itemList = new ObservableCollection<PmemCombItem>();
_itemList.CollectionChanged += new NotifyCollectionChangedEventHandler(OnCollectionChanged);
L_PmemCombList.ItemsSource = _itemList;
In order to avoid refreshing the complete ListView I do a simple comparison of the newly retrieved list with the current _itemList, change items which are not the same and add/remove items if necessary. The collection "newList" contains newly created objects, so replacing an item in _itemList is correctly sending a "Refresh" notification (which I can log by using the event handler OnCollectionChanged of the ObservableCollection`)
Action action = () =>
{
for (int i = 0; i < newList.Count; i++)
{
// item exists in old list -> replace if changed
if (i < _itemList.Count)
{
if (!_itemList[i].SameDataAs(newList[i]))
_itemList[i] = newList[i];
}
// new list contains more items -> add items
else
_itemList.Add(newList[i]);
}
// new list contains less items -> remove items
for (int i = _itemList.Count - 1; i >= newList.Count; i--)
_itemList.RemoveAt(i);
};
Dispatcher.BeginInvoke(DispatcherPriority.Background, action);
My problem is that if many items are changed in this loop, the ListView is NOT refreshing and the data on screen stay as they are...and this I don't understand.
Even a simpler version like this (exchanging ALL elements)
List<PmemCombItem> newList = new List<PmemCombItem>();
foreach (PmemViewItem comb in combList)
newList.Add(new PmemCombItem(comb));
if (_itemList.Count == newList.Count)
for (int i = 0; i < newList.Count; i++)
_itemList[i] = newList[i];
else
{
_itemList.Clear();
foreach (PmemCombItem item in newList)
_itemList.Add(item);
}
is not working properly
Any clue on this?
UPDATE
If I call the following code manually after updating all elements, everything works fine
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
But of course this causes the UI to update everything which I still want to avoid.
After a change, you can use the following to refresh the Listview, it's more easy
listView.Items.Refresh();
This is what I had to do to get it to work.
MyListView.ItemsSource = null;
MyListView.ItemsSource = MyDataSource;
I know that's an old question, but I just stumbled upon this issue. I didn't really want to use the null assignation trick or the refresh for just a field that was updated.
So, after looking at MSDN, I found this article:
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged?redirectedfrom=MSDN&view=netframework-4.7.2
To summarize, you just need the item to implement this interface and it will automatically detect that this object can be observed.
public class MyItem : INotifyPropertyChanged
{
private string status;
public string Status
{
get => status;
set
{
OnPropertyChanged(nameof(Status));
status = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
So, the event will be called everytime someone changes the Status. And, in your case, the listview will add a handler automatically on the PropertyChanged event.
This doesn't really handle the issue in your case (add/remove).
But for that, I would suggest that you have a look at BindingList<T>
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.bindinglist-1?view=netframework-4.7.2
Using the same pattern, your listview will be updated properly without using any tricks.
You should not reset ItemsSource of ListView each time observable collection changed. Just set proper binding that will do your trick. In xaml:
<ListView ItemsSource='{Binding ItemsCollection}'
...
</ListView>
And in code-behind (suggest to use MVVM) property that will be responsible for holding _itemList:
public ObservableCollection<PmemCombItem> ItemsCollection
{
get
{
if (_itemList == null)
{
_itemList = new ObservableCollection<PmemCombItem>();
}
return _itemList;
}
}
UPDATE:
There is similar post which most probably will Answer your question: How do I update an ObservableCollection via a worker thread?
I found a way to do it. It is not really that great but it works.
YourList.ItemsSource = null;
// Update the List containing your elements (lets call it x)
YourList.ItemsSource = x;
this should refresh your ListView (it works for my UAP :) )
An alternative on Xopher's answer.
MyListView.ItemsSource = MyDataSource.ToList();
This refreshes the Listview because it's a other list.
Please check this answer:
Passing ListView Items to Commands using Prism Library
List view Items needs to notify about changes (done is setter)
public ObservableCollection<Model.Step> Steps
{
get { return _steps; }
set { SetProperty(ref _steps, value); }
}
and UpdateSourceTrigger need to be set in xaml
<Image Source="{Binding ImageData, UpdateSourceTrigger=PropertyChanged}" />
Earlier today i was suggested in here to use a DataGridView to print messages that needed a individual mark as read.
I followed the suggestion, and with some reading online i managed to bind it to my message list with the following results after some tweaking.
alt text http://img237.imageshack.us/img237/3015/datagridview.jpg
Currently i have 2 issues, the first one is that i didn't find a way to resize the row height to display the full message, and the second one is that when the list is updated, the DataGridView doesn't display the modifications.
Any way to solve both problems? Or do i need to use something other than DataGridView, and in that case what should i be using?
Also, is there any way to urls contained in the message to become clickable and be opened in the default browser?
EDIT
More info in relation to the binding.
Basically i have a class variable inside the form, and i do the initial binding with a button.
private void button1_Click(object sender, EventArgs e)
{
list.Add(new Class1() { Message = "http://www.google.com/", Read = false });
list.Add(new Class1() { Message = "Message way too long to fit in this small column width", Read = false });
dataGridView1.DataSource = list;
}
I then have another button that adds some more entries just to test it, and i know the list is properly updated, but there are no changes in the dataGridView.
EDIT 2
If i wasn't clear before i need for the width to be fixed, and the cell height that contains the long text to be enlarged and display the text in 2 lines
have you checked the options in the EditColumn using smart tag ?
you can add column of type
DataGridViewLinkColumn, set its Text property to Message
Try removing any value from width
and height properties for a
column. In this way, it will set the
column size (cell) size according to
the data size.
hope this helps
I'll take a stab and see if I can help.
First off the row height. There are two DataGridView Methods called AutoResizeRow and AutoResizeRows which will adjust the height of the row to fit the contents.
Can you show us how you are binding your data to the DataViewGrid and how the data might be modified? That will help with the modifications not updating.
As for the link, unfortunately I can't seem to find an object which handles this sort of thing natively. Most likely you will first have to decide if the text going into the DataGridView is a link (using a Regular Expression, if you were me). Second, display it differently in the DataGridView (underline it, make it blue). Third, put a click event on it and when that cell is clicked handle that by throwing it out to a browser. I will look a little further into it though since this seems like a lot of work (and I will keep my fingers crossed that someone knows better than I do).
Regarding the list not updating; there are two issues;
To notice add/remove, you need list binding events. The easiest way to do this is to ensure you use a BindingList<YourClass> rather than a List<YourClass>.
To notice changes to individual properties (in this context) you'll need to implement INotifyPropertyChanged on your type:
public class YourClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private string message;
public string Message
{
get { return message; }
set { message = value; OnPropertyChanged("Message"); }
}
public bool isRead;
[DisplayName("Read")]
public bool IsRead
{
get { return isRead; }
set { isRead = value; OnPropertyChanged("IsRead"); }
}
}
For an example showing binding that to a list:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
BindingList<YourClass> list = new BindingList<YourClass>();
DataGridView grid = new DataGridView();
grid.Dock = DockStyle.Fill;
grid.DataSource = list;
Button add = new Button();
add.Text = "Add";
add.Dock = DockStyle.Bottom;
add.Click += delegate
{
YourClass newObj = new YourClass();
newObj.Message = DateTime.Now.ToShortTimeString();
list.Add(newObj);
};
Button edit = new Button();
edit.Text = "Edit";
edit.Dock = DockStyle.Bottom;
edit.Click += delegate
{
if (list.Count > 0)
{
list[0].Message = "Boo!";
list[0].IsRead = !list[0].IsRead;
}
};
Form form = new Form();
form.Controls.Add(grid);
form.Controls.Add(add);
form.Controls.Add(edit);
Application.Run(form);
}