Detecting dirty using winforms data binding - c#

I am using 2 way binding with winforms text boxes.
I need to work out if the user has changed my data
Looking at the help for
the CurrentItemChanged Event
It seems that this event does fire if a property has changed, however it also fires if current has changed.
Is there a way to tell whether the data has changed?
a similar question is also asked here
but not answered in my opinion
Oliver mentions "if your object within the List support the INotifyPropertyChanged event and you replace the List by a BindingList you can subscribe to the ListChanged event of the BindingList to get informed about any changes made by the user."
My application meets these conditions but I cant get this working. The ListChangedType.ItemChanged property looked hopeful, but it changes when I navigate to the next record without changing the data
I found a link at Microsoft here but surely it cant be that hard!

This seems to work
void bindingSource_BindingComplete(object sender, BindingCompleteEventArgs e)
{
if (e.BindingCompleteContext == BindingCompleteContext.DataSourceUpdate)
{
var person = (Person)bindingSource.Current;
if ( person.State == State.Unchanged && (e.BindingCompleteState == BindingCompleteState.Success)
&& e.Binding.Control.Focused)
{
person.State = State.Modified; // using Julie Lerman's repositories technique
}
}
}

Related

WPF Modern Tab Links

I am trying to work with Modern UI, namely Modern Tab control.
I have created a LinkList of Pages(UserControls) and is popuating my tabcontrol with these, since I will be fetching data from a database, and thus it is not possible for me to know how many links the user will be presented with.
My problem is, binding an event to the Link, and loading content according to the selected Link.
I have tried to use SelectedSourceChanged="{Binding SelectedKalenderChanged}", but this is not possible.
I have then tried to call the event behind-code, however the event only fires off once, and I get no updates when the user selects another link.
Here is the method behind-code;
private void KalenderController_OnSelectedSourceChanged(object sender, SourceEventArgs e)
There is no e.Handled possibility.
Anyone have a suggestion on how I can raise a propertychanged event when the user selects another link?
Thank you in advance for any help/suggestions
I found out what i was doing wrong, i was naming my page uri on list creation with the same name.
By doing this;
public MainViewModel()
{
LinkList = new LinkCollection();
KalenderList = new ObservableCollection<Kalender>();
KalenderList = DataHandler.HentKalendere();
foreach (var kalender in KalenderList)
{
LinkList.Add(new Link
{
DisplayName = kalender.Navn,
Source = new Uri("/Pages/UgePage.xaml#"+kalender.Navn, UriKind.Relative)
});
}
_selectedKalender = LinkList[0].Source;
}
i am now able to detect which link was selected.

preselect checkboxes in radtreeview silverlight)

I have a silverlight app where there is a telerik radtreeview with checkboxes. The user selects stuff and when the user wants to edit it's selection i need to pre-populate the tree with the previously saved selection.
I found out that I can bind the checkboxes to my viewmodel. But if I choose that scenario I don't use the "built in" checkboxes and lose the tristate logic (autoselecting siblings when selecting a parent and such)
So I am experimenting with trying to get the radtreeviewitem objects from the radtreeview.items collection
http://www.telerik.com/help/silverlight/radtreeview-how-to-iterate-through-treeviewitems.html
The problem is that the radtreeviewitems are only generated when a node is expanded by a user in the ui. So not all items I want to iterate through are present after the control is databound.
I have not found a good way to force the ui to build all the radtreeviewitems so I can iterate through them and set my preselection. I found the links below but it only seems to work with the root node, not the siblings.
WPF: control.ItemContainerGenerator.Status is NotStarted. How do I tell it to start now?
Would you guys also consider rebuilding the "tristate-mode" into your viewmodel logic "dirty"?
How would you go about preselecting checkboxitems in the radtreeview?
This is how I do it :
public static void CheckAllTreeItemsAuto(RadTreeView tree)
{
tree.ItemContainerGenerator.StatusChanged += (s, e) =>
{
if ((s as Telerik.Windows.Controls.ItemContainerGenerator).Status == Telerik.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
RadTreeViewItem item = (RadTreeViewItem)tree.ItemContainerGenerator.ContainerFromIndex(0);
while (item != null)
{
item.IsChecked = true;
item = item.NextItem;
}
}
};
}
I didn't experience your problem with the items not generated at the start. (I don't know how you generate your RadTreeView).
When working with the RadTreeView control you need to have in mind that the built-in tri-state logic is designed to work with declaratively defined control and items, only. This means that using this feature in MVVM scenarios will not work as expected.
Since Telerik is aware of that limitation they provided the community with an article demonstrating how developers can use the tri-state logic of a native CheckBox control in MVVM scenarios. You can find the article in their documentation. Also, at the end of the article you can find a link leading to their CodeLibrary where you can download ready to run project demonstrating the described approach.
I hope this information will help you.

Reopen Silverlight AutoCompleteBox popup without reloading suggestions from webservice

I have an AutoCompleteBox binded to an ObservableCollection ItemsSource which I filter on my own by querying entities from a domainservice.
I used the scenario of populating from a webservice call from the blog of Jeff Wilcox, by setting the PopulatingEventArgs.Cancel to True, and when my collection is ready, I call PopulateComplete() on the ACB.
My goal is to reopen the dropdown on mouseover (or click) but without reloading again all the data from the web. I found a question on stackoverflow where the answer was to set IsDropDownOpen to True. But in this case, the ACB population starts again, and another call goes to the webservice.
Of course, when the user starts typing, the filtering should be done again.
(for ex. you type "ric" and the box suggests "rice" and "ricin", you select rice, but you change your mind and want to select another one from the same collection, lets say "ricin". In this case you already have the suggestions containing "ric" in memory, no need to load them again..)
I found an alternative way in which instead of setting IsDropDownOpen, I just simply call the PopulateComplete() method. This does exactly the same thing that I want, but with a little fail: after my ACB loses focus, the dropdown is not opened again on mouseover liek it should. Even when I click back into the acb textbox.
So is there a fix for this, or does someone know why the PopulateComplete() only reopens the dropdown when the ACB has focus for the first time? Or this was only my luck that calling this method reopened the dropdown and the IsDropDownOpen property should be used instead (afaik this would be only possible with some flags indicating that its a fake populating event triggered by my mouseover and after PopulatingEventArgs.Cancel i should call immediately PopulateComplete. but i dont get it, if this may work (haven't tried yet), why not when calling simply the PopulateComplete)?
Well, I tried the IsDropDownOpen with a testing bit, and almost worked:
private void FoodBox_MouseEnter(object sender, MouseEventArgs e)
{
//FoodBox.PopulateComplete(); not working after acb loses focus...
testbit = true;
FoodBox.IsDropDownOpen = true;
}
Here's the overloaded Populating method (no need for setting ItemsSource explicit because its bound to an ObservableCollection):
public void FoodBox_Populating(object sender, PopulatingEventArgs e)
{
e.Cancel = true;
if (!testbit)
{
VM.LoadFoodSuggestions(FoodBox.SearchText);
}
else
{
testbit = false;
FoodBox.PopulateComplete();
}
}
This works good so far, execpt that the search does not start because when (for the first time) you mouseover and select the acb, it sets the testbit to true.
So I added another event handler that takes care of setting the testbit to false every time the user inputs text on the keyboard, ensuring that the suggestions are regenerated/reloaded after SearchText is modified by the user, but not when you select an item from the dropdown:
private void FoodBox_TextChanged(object sender, RoutedEventArgs e)
{
testbit = false;
}
I still don't know why calling PopulateComplete() isn't enough without setting the IsDropDownOpen to Ture, and setting that to true, also delays the dropdown opening approximately with the time specified in the MinimumPopulateDelay, but at least it gives me the functionality I wanted. (Maybe digging into the source of acb would answer this mistery)
Maybe this functionality implemented in the basic acb would be helpful in a future release of the control.

Custom event on object to change button state

I have 3 buttons on my Data Entry form, OK, APPLY, CANCEL.
This form is used to edit a doctor's details, things like first name, phone # etc...
I have one object doctorObj which at any given time is either empty (a new doctor) or an object pulled from a Linq query.
I deep clone the doctorObj to EditCopyDoctor which is of the same type but used for form editing (so if Cancel is hit, the database do not need to update).
What I want to achieve is observe the EditCopyDoctor for changes against the original doctorObj so
If(doctor.Changed() && doctor.IsNotNew)
{
ApplyButton.Enabled = true;
}
else
{
ApplyButton.Enabled = false;
}
So I thought writting an event to trigger when something changed on EditCopyDoctor is the best way to do it but I'm not sure how.
I can of course put the ApplyButton.Enabled code in the TextChanged events from the form but I was wondering if there are any quicker ways to do this, I don't really want to do this as there are 10+ textbox and other controls.
Since your "Doctor" type sounds like it's generated by LINQ to SQL/Entities you should find that it already implements the INotifyPropertyChanged interface. Therefore, you should just need to watch the PropertyChanged event and act accordingly.

Disabling a TextBox in C# .NET using CSLA

I am trying to disable a number of text boxes intended for displaying data (not edit) in one of my UserControls. However, for some reason I can not get the textBoxes to disable properly.
I've set "ApplyAuthorization on readWriteAuthorization" to true and the textBoxes are databound to the correct properties.
I've also added the following lines to the CanWriteProperty of my object:
if (propertyName == OpeningDateProperty.Name) return false;
if (propertyName == ChangeDateProperty.Name) return false;
if (propertyName == CloseDateProperty.Name) return false;
return base.CanWriteProperty(propertyName);
I can't figure out what I'm doing wrong here. I've implemented pretty much the same thing recently in other UserControls without any problems...
I am using Windows Forms in C# .NET (Visual Studio 2008)
EDIT: The code snippets and the properties are taken from my customer object. The date represent opening, last change and closure of the customer account. They are never supposed to be edited at all and in fact in the old sollution they are represented by textLabels, however we now want to use a text box and make the property's CanWriteProperty false.
I realise that the information might be sort of scarce, but I am looking for what I might have forgotten in this process.
EDIT: We are using CSLA as well and I guess (I'm new at this whole thing) this has something to do with why we want to do it like this.
EDIT (Sollution): As you can see in my answer below, the problem was that I had not set up the CurrentItemChanged event like I should have.
If you're trying to get them to be read only, then just set the .ReadOnly property to true.
Alternatively, if you're never ever using these textboxes for editing, then maybe just use a Label instead?
EDIT: Ahh it appears this more of a CSLA-framework question than a pure windows forms question. I've never even heard of CSLA before this question, but it looks interesting.
If you are databinding to properties of the control just bind the "ReadOnly" property of the textbox to the "CanWrite" property of your business object.
i think you mean ReadOnly property
To make this work you need to do the following:
Make sure the TextBox is databound to the right property in the correct way
Set up the needed checks for each textBox in the CanWriteProperty override in your root object
if (propertyName == OpeningDateProperty.Name) return false;
Make sure the rootBindingsource's CurrentItemChanged event is set up right
private void rootBindingSource_CurrentItemChanged(object sender, EventArgs e)
{
readWriteAuthorization1.ResetControlAuthorization();
}
Make sure the texBox's "ApplyAuthorization on ReadWriteAuthorization" is set to true
This solved the problem for me.

Categories

Resources