i am developing an application in wpf using MVVM design pattern. i have a listbox when an item is slected then a dialog is open having the same record in editable mode. this dialog is binded with the selected item of the list. i have apply the validation rule for textbox using IDataErrorInfo. when the user update a record on dialogbox then at every key press, the selected record in listbox is also changed. if the user press save button then i submit changes to database. but if user click cancel button then i do not submit changes to database but the list box is updated with the current updation in GUI. when i refresh the list then old value appears again. My requirement is to update the listbox only when the user hit the save button but not on every key press on dialog box. I first fill the generic list with the linq to sql classes then bind the listbox with it. Please let me know what i have to do.
Thanks in advance
The problem is that you are editing the same object on both forms. You should pass the SelectedItem to the dialog form, but then re-query the database for the item that was passed to the constructor. This does two things: allows you to cancel the changes when the object has been edited, and provides the user with the most current data from the database.
Think of it this way... If the listbox contained data that was even a few minutes old, your user would be modifying data that may have already changed by another user running your application.
Once the user saves (or deletes) the record in the dialog form, you must then refresh the listbox. Typically I use the following method:
DialogViewModel:
// Constructor
public DialogViewModel(MyObject myObject)
{
// Query the database for the required object
MyObject = (from t in _dc.MyObjects where t.ID == myObject.ID
select t).Take(1).Single();
}
// First define the Saved Event in the Dialog form's ViewModel:
public event EventHandler Saved;
public event EventHandler RequestClose;
// Raise the Saved handler when the user saves the record
// (This will go in the SaveCommand_Executed() method)
EventHandler saved = this.Saved;
if (saved != null)
saved(this, EventArgs.Empty);
ListBox ViewModel
Views.DialogView view = new Views.DialogView();
DialogViewModel vm = new DialogViewModel(SelectedItem); // Pass in the selected item
// Once the Saved event has fired, refresh the
// list of items (ICollectionView, ObservableCollection, etc.)
// that your ListBox is bound to
vm.Saved += (s, e) => RefreshCommand_Executed();
vm.RequestClose += (s, e) => view.Close();
view.DataContext = vm;
view.ShowDialog();
Related
I have a bind object of observable collection to my data grid view. But I am not able to figure out where issue exactly occurs. As I bind collection objects to grid views and when I add an item from list control in active grid view (Grid present in one tab), at the same time all grid views get reflected with newly added record in all tabs.
i have created 2 view models and 2 user controls. 1st (common view) user control contains ComboBox control.
2nd view contains Gridview.
View2.cs=
View2ViewModel _vIewModel= new View2ViewModel(EventAggrtorService.Instance.EventAggregator, _vIewModel,DataCollection);
this.mydtGrid.ItemsSource = _vIewModel.DataCollection;
public ObservableCollection DataCollection = new ObservableCollection();
In 1st view model i have publish event to send selected item from combobox control and 2nd view model have subscriber.logic to insert data in gridview.
Problem occurs when i click on ribbon button (it create new window and executes 2ndViewModel constructor) for new tab to open then that new window got subscribed for event i publish from view 1. hence all views calls logic to InsertData i selected from combo control.hence all views shows new record.
public 2ndViewModel(IEventAggregator evAggrtor ,ObservableCollection DataCollection)
{
this._eventAggregator = evAggrtor;
this.DataCollection =DataCollection;
SubscriptionToken token = _eventAggregator.GetEvent<SelectedItemEvent2>().Subscribe(FillLookUpObject,
Microsoft.Practices.Composite.Presentation.Events.ThreadOption.PublisherThread);
}
One of those 'Why is this so hard?" questions.
I have a ListBox (containing details of share portfolios). The listbox item uses a grid to display attributes of the portfolio. Source is a list of portfolios in the View Model.
ListBox is multiselect - when selection changes, a list of the constituents of the selected portfolios is re-populated.
What I want to do is put a button (or menu or whatever) on the listboxitem to display a list of possible actions (Trade, Unitise, Delete etc).
When an action is selected I need to execute the action against the appropriate portfolio. Ideally I want the actions to be available for both selected and unselected items.
I can handle the event, but how do I detect which item (portfolio) the user selected? I've looked at GotFocus() but it doesn't seem to fire.
In other words if a control in a Listboxitem, fires an event, how does the event 'know' which ListBoxItem raised it?
For me, the solution here, seen as you mentioned MVVM, would be to have the ListBox populated by a collection of ViewModels, e.g., something like ObservableCollection<PortfolioViewModel>.
It would then just be a case of binding the Command property of the Button to an ICommand on the ViewModel that executes whatever work you need doing.
I can handle the event, but how do I detect which item (portfolio) the user selected? I've looked at GotFocus() but it doesn't seem to fire.
You could cast the DataContext of the clicked Button to the corresponding object in te ListBox, e.g.:
private void DeleteButton_Clicked(object sender, RoutedEventArgs e)
{
Button deleteButton = sender as Button;
var portfolio = deleteButton.DataContext as Portfolio; //or whatever your type is called
//access any members of the portfolio...
}
I have binded ObservableCollection to the itemsource of the datagrid,
Now for each and every operation performed on the datagrid, i am storing the changes in the undo-redo stack, we have two toolbar buttons for undo and redo, so we are supporting undo-redo on click of these buttons, now we have a requirement, where we want to provide a new button which should Undo All grid changes in one click, it should clear the undo stack.
At present, i am making the call to the business logic to get the original data and reloading the datagrid, as the original data which i have sent to the grid is modified and the changes are accepted.
I am trying to see if i can get the original state of the data at the initial load using undo-redo stack with out multiple refreshes in the datagrid [ user should not feel multiple refreshes hapenning]?
I can maintain a copy of the data before loading the data to the datagrid, but want to know if i can achieve this by the undo-redo stack or by any functions of observablecollections?
...
private myDBDataContext dc = new myDBDataContext(); //context DB
public MainWindow()
{
InitializeComponent();
//binding the datagrid (WPF) with context db and table
if (dc.DatabaseExists())
{
dataGrid.ItemsSource = dc.myTables;
}
}
private void Refres_Click(object sender, RoutedEventArgs e)
{
//call the refresh with "OverwriteCurrentValues" (this is what you need)
dc.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, dc.myTables);
}
I am using SL4, RIA toolkit Spt 2011, SP1. MVVM
When my text box is directly from the parent table, upon change on the text the commit button gets enabled but when the text box is from the foreign key and when we changes are there submit buttons still remains disabled.
Have read this DataForm commit button is not enabled when data changed which is not similar to my problem.
my database table look likes
Customertable (name, IdEmail) and EmailTable(IdEmail, Email)
Dataform is like:
Name: _____________
Email: _____________
|submit| |Cancel|
But cancel button is always active. I want submit button to be active when ever i have some changes in email. however if i have changes in name the submit button get enabled and i could also save the changes in email after that.
Both Name and email has two way binding with viewmodel.
for example view model is like
private ObservableCollection<Customertable > _customer
public ObservableCollection<Customertable > Customer
{
get return _customer;
}
set{
if(_customer == value) return;
_customer = value;
RaisedProtertyChanged("Customer");
}
Email field in above "view" is binded into "Customer.EmailTable.Email"
Or is only way to solve this is to make my own custom submit and cancel button?
Without seeing the rest of your code, I'm taking a couple shots in the dark. I assume you're beginning an edit or putting the form in add-new mode, since your cancel button is active? If that's not the issue, the easiest fix is probably manually enabling (and disabling, if needed) the submit button. Perhaps doing so with VisualTreeHelper, as suggested in this SL forums thread would do the trick?
Old question for outdated technology. Perfect time to answer:
Extend the Dataform so that the command button is always enabled.
public class CustomDataForm : DataForm
{
private Button _commitButton;
protected override void OnContentLoaded(DataFormContentLoadEventArgs e)
{
base.OnContentLoaded(e);
var dependencyObject = VisualTreeHelper.GetChild(this, 0) as Grid;
_commitButton = dependencyObject?.FindName("CommitButton") as Button;
if (_commitButton == null) return;
_commitButton.IsEnabled = true;
_commitButton.IsEnabledChanged += (s, e1) =>
{
if (!(bool) e1.NewValue)
_commitButton.IsEnabled = true;
};
}
}
I wonder how you do such thing. Assume, we have MVVM CRUD app which modifies a tree (menu structure, for example). We have a view model with the menu items and two views: the first with a TreeView and the second with a DataForm. Main problems are:
DataForm can not handle
hierarchical data.
Depending on the menu item selected
in the TreeView the DataForm
should display different set of
fields (for example for menu items
with children or without).
I've ended up with the following. View model has 3 fields:
Items — the collection of
MenuItem objects which have their
own Children collection for
building hierarchical data source.
SelectedItem — currently selected
MenuItem in the TreeView.
EditedItem — EditViewModel
object which basically has two
descendants: MenuItemEditViewModel
and LeafMenuItemEditViewModel.
This property is set automatically
when SelectedItem is changed. Its
actual type is inferred from the
SelectedItem.Children emptiness.
TreeView is bound to Items and SelectedItem. DataForm is not required to maintain currency in this case (instead current item is set by the TreeView) nor it is responsible for creating and deleting items. That's why I decided to bind only its CurrentItem to view model's EditedItem (ItemsSource is unbound). Its AutoCommit is set to False (when it is True and ItemsSource is unbound all current item changes get copied to newly selected item when you select different item in the TreeView, which is not so nice). DataForm fields are autogenerated.
Obviously, that now if we select an item in the TreeView, then make some changes in the DataForm and try to select different item in the TreeView we'll get well-known
Cannot change currency when an item
has validation errors or it is being
edited and AutoCommit is false. Set
ItemsSource to a ICollectionView to
manage currency instead
In this case I want DataForm to discard all changes implicitly. There is a workaround to call DataForm.CancelEdit() before TreeView selected item is changed (usually an event like PreviewSelectionChanged or BeforeSelectionChanged). But it is not the MVVM way since the TreeView and the DataForm are defined in completely different views (read: is not acceptable).
Is there something like AutoCancel which forces DataForm to cancel changes when its CurrentItem is changed? Maybe someone from dev team can answer? Or how would you deal with such problem?
I was surprised to find the Silverlight is severly lacking in this functionality, considering all the business oriented RIA functionality. AutoCommit is not acceptable to me because I want the user to explicitly acknowledge pending changes, rather than just commit something to the database that they may not want.
You can reliably track the edit mode of the DataForm using a private member variable and trapping the BeginningEdit and EditEnded events of the DataForm (naming inconsistency! Why one is called xxxEdit and the others are Editxxx is beyond me. Should it not be EditBeginning and EditEnded??). Inside the event handler for BeginningEdit, set the flag to true and set it to false in EditEnded.
In your SelectionChanged event, you can then check the flag. If it is true, you can call the CancelEdit on the DataForm.
private bool _editing = false;
public MainPage() {
DataForm1.BeinningEdit +=
new EventHandler<CancelEventArgs>(DataForm1_BeginningEdit);
DataForm1.EditEnded +=
new EventHandler<DataFormEditEndedEventArgs>(DataForm1_EditEnded);
}
protected void DataForm1_BeginningEdit(object sender,
System.ComponentModel.CancelEventArgs e) {
_editing = true;
}
protected void DataForm1_EditEnded(object sender,
DataFormEditEndedEventArgs e) {
_editing = false;
}
void TreeView1_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
{
if (_editing) {
object previous = DataForm1.SelectedItem;
object current = TreeView1.SelectedItem;
if (MessageBox.Show("Are you sure you want to cancel the changes?",
"Confirm", MessageBoxbutton.OKCancel) == MessageBoxResult.OK) {
DataForm1.CancelEdit();
}
else {
TreeView1.SelectedItem = previous;
}
}
}
Have you tried to set AutoCommit at True ?