Windows Form Dropdown how to get previously selected value - winforms

I come from web background, now developing a windows application.
Application has drop down which shows the selected item (which comes from DB). I've a requirement that on selection of any other value than existing one perform xyz business logic.
I could use the index change event handler however, if they select other items and select back same I don't want to perform xyz business logic.
Therefore, can someone please help how it's possible to compare the selected value with one selected on load?
I could have session to store the previous state but not sure how can we do same in windows form.

In this case, your concept of session storage is a local storage variable. An example class:
public class MyForm : Form
{
private Int32 _selIdx = -1; //this is local to the Form and accessible anywhere within the MyForm class
public MyForm() { }
private void MyForm_Load(Object sender, EventArgs e)
{
//load database data, add to combo box, then capture the index
_selIdx = myComboBox.SelectedIndex;
}
//I prefer this method because it reacts to user interaction, and not programmatic change of index value
private void myComboBox_SelectionChangeCommitted(Object sender, EventArgs e)
{
//index changed, react
if (!_selIdx.Equals(myComboBox.SelectedIndex))
{
//not a match, do something...
}
}
}

Related

Windows Phone Navigation - Passing back to the page before

I would like to click on a button to take me to a page
, then click on a listbox item, click on a button on the new page and pass it back to the page before without creating a new URI of the first page.
**First Page**
private void btnAddExistingMember_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/ChooseMember.xaml", UriKind.Relative));
}
**Second page after choosing listbox value**
private void btnAddSelected_Click(object sender, RoutedEventArgs e)
{
Member currMember = (Member)lstMembers.SelectedItem;
string memberID = currMember.ID.ToString();
//navigate back to first page here passing memberID
}
can it be done?
Thank you
You can store the member in the App.xaml.cs file. This is common file accesssible for all files in the application.
This works like a global variable.
//App.xaml.cs
int datafield ;
//Page1xaml.cs
(App.Current as App).dataField =10;
//Page2.xaml.cs
int x = (App.Current as App).dataField
You could create a manager class that would hold the member id. This manager class could then be accessed from both your first page and ChooseMember page.
An example of a Singleton Manager class :-
public class MyManager
{
private static MyManager _instance;
public static MyManager Instance
{
get
{
if (_instance == null)
{
_instance = new MyManager();
}
return _instance;
}
}
}
I found a solution at codeproject which was quite useful to me.
While moving from the second form, save your data in PhoneApplicationService.Current.State
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
// Called when a page becomes the active page in a frame
base.OnNavigatedFrom(e);
// Text is param, you can define anything instead of Text
// but remember you need to further use same param.
PhoneApplicationService.Current.State["Text"] = txtboxvalue.Text;
}
Go back using same NavigationService.GoBack(); and in OnNavigatedTo method, fetch the following code.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (PhoneApplicationService.Current.State.ContainsKey("Text"))
txtvalue.Text = (string)PhoneApplicationService.Current.State["Text"];
}
References:
MSDN: PhoneApplicationService Class
Original Solution at: How to pass values between pages in windows phone
Sounds to me like you want to set some object as the context for another page. Messaging in MVVM Light sounds like a good solution for this. Doesn't look like you're using MVVM so this may not be immediately applicable. This post pretty much lays out what I'm saying here.
Second Page
Create your SelectedObject property and make sure to call
RaisePropertyChanged(SelectedObjectPropertyName, oldValue, value, true);
The last parameter true says to broadcast this change in value to anyone listening. You'll need to wire up some other commands for listbox selected item and button click etc, but I won't go into that here since it's not directly related to your question. Selecting the Listbox item will simply set the data item for the first page like you want to accomplish. The button click can deal with the navigation.
First Page
In your view model constructor, register to receive the change broadcasted from Second Page
Messenger.Default.Register<PropertyChangedMessage<MyObject>>(this, (action) => UpdateObject(action.NewValue));
then define UpdateObject
private void UpdateObject(MyObject newObject)
{
LocalObjectProperty = newObject;
}
You can simply use
//first page
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string value = string.Empty;
IDictionary<string, string> queryString = this.NavigationContext.QueryString;
if (queryString.ContainsKey("memberID"))
{
memberID = queryString["memberID"];
if (memberID != "-1")
//your code here
}
base.OnNavigatedTo(e);
}
//second page
private void btnAddSelected_Click(object sender, RoutedEventArgs e)
{
Member currMember = (Member)lstMembers.SelectedItem;
string memberID = currMember.ID.ToString();
string target = "/FirstPage.xaml";
target += string.Format("?memberID={0}", memberID);
NavigationService.Navigate(new Uri(target, UriKind.Relative));
}

Sort "Master-Detail" DataGridView bound to Entity Framework in Winforms

I'm writing a small business application in C#, .NET 4.0. I am using SQL Server CE 4.0 as my database.
I use Entity Framework to communicate in both directions with database. My datagridviews are bound to Entity Framework collection, thus user can add new or modify existing data directly in datagridview. The problem is that sorting with Entity Framework bound to datagridview is not really supported. From what I have learned:
I can intercept clicks to column header cells and then perform sorting and rebind result to datagridview. It is a bit tedious, but it works for master datagridview. But when I do that also for "detail" datagridview" then I loose the automatic rebinding of "detail" datagridviews (when new row from master table is selected). So I have to deal with that also.
I can cast query to a list / binding list and pass it to a sortable bindinglist. Well here I have the same problem with rebinding "detail" datagridviews manually. The new problem that here arises is, that now I have to somehow fix saving, because new data is added only to the sortable bindinglist and not directly to the Entity Framework context.
What should I do (and how)? Should I just use DataSets?
My preference is to use an intermediate [observable]collection which holds the sorted entities. However this is in the WPF/MVVM world. And even then the Pattern is still relatively the same for ASP.NET ObjectDataSource or MVC using Collections. It's been quite some time but maybe I can brain dump a bit where hopefully you can find something useful.
I'm pulling this from memory so bare in mind this is nothing more to help point you in some direction.
Form variables we'll use.
private string SortProperty { get; set; }
private ListSortDirection SortDirection { get; set; }
private ICollection<myItems> items; // Entity Collection
private ObservableCollection<myItems> SortedItems { get; set; } // Sorted Collection
Overload the form OnLoad event handler to register a header clicked handler to apply sorting.
protected override void OnLoad(EventArgs e)
{
dataGridView1.ColumnHeaderMouseClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(dataGridView1_ColumnHeaderMouseClick);
LoadDataGridView();
base.OnLoad(e);
}
protected override void OnUnload(EventArgs e)
{
dataGridView1.ColumnHeaderMouseClick -= new System.Windows.Forms.DataGridViewCellMouseEventHandler(dataGridView1_ColumnHeaderMouseClick);
base.OnUnload(e);
}
Perform our initial load of the sorted data.
private void LoadDataGridView()
{
items = myRepository.GetAllItems(); // However you get or have your collection of items.
ApplySort();
dataGridView1.DataSource = SortedItems;
}
Sort our data and save in new collection. The OrderBy requires Dynamic Query Library: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx.
private void ApplySort()
{
// IQueryable<myItems>, ICollection<myItems>, ObservableCollection<myItems>... be aware of cross threading and how you will handle updates.
SortedItems = items.AsQuerable().OrderBy(SortProperty + (SortDirection == ListSortDirection.Ascending ? " asc" : " desc")).ToList();
}
Our click event handler. Remember, you will have to handle entities added, deleted and changed.
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
var propertyName = GetPropertyName(dataGridView1.Columns[e.ColumnIndex])
SortDirection = SortProperty == propertyName ?
SortDirection == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending
: SortDirection;
SortProperty = propertyName;
ApplySort();
dataGridView1.DataSource = SortedItems;
}
Trivial helper method for sorting.
private string GetPropertyName(int columnNumber)
{
switch(columnNumber)
{
case 0:
return "Id";
case 1:
return "Name";
default:
return "Id";
}
}
Here is some additional information:
http://msdn.microsoft.com/en-us/library/ms171608.aspx
I know this isn't an exact answer but since no one else will chime in, maybe you can take this, hack it up until it works gracefully and then comment your solution for the next person.

Detect when a row is edited in a DataGrid

I've been trying to google this but have been unable to find a solution that works for me.
I have a DataGrid that is displaying some info from a SQL table that the client dosn't know about.
The client just sends a request to the server and gets a List<SomeClass> as a response that it then displays in a DataGrid.
I need to detect when the user makes change to a row and I need the new values that the user entered.
Currently I'm using RowEditEnding event. And the method that handles this event can then:
private void editRowEventHandler(object sender, DataGridRowEditEndingEventArgs e)
{
SomeClass sClass = e.Row.DataContext as SomeClass;
// Send sClass to the server to be saved in the database...
}
This gives me the row that was being edited. But it gives me the row before the changes, and I'm unable to figure out how to get the row after the changes happen.
Is there anyone here that knows how I can do this or can point me in a direction where I might be able to find out?
See the discussion here, to avoid reading out cell-by-cell.
private void OnRowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
if (e.EditAction == DataGridEditAction.Commit) {
ListCollectionView view = CollectionViewSource.GetDefaultView(dataGrid.ItemsSource) as ListCollectionView;
if (view.IsAddingNew || view.IsEditingItem) {
this.Dispatcher.BeginInvoke(new DispatcherOperationCallback(param =>
{
// This callback will be called after the CollectionView
// has pushed the changes back to the DataGrid.ItemSource.
// Write code here to save the data to the database.
return null;
}), DispatcherPriority.Background, new object[] { null });
}
}
}
In your case, you are trying to detect the change in object. It comes down to the properties of the SomeClass, thus you need to focus on "Cell" instead of "Row"
Assuming your datagrid is resultGrid, i come up with the below code:
resultGrid.CellEditEnding += resultGrid_CellEditEnding;
void resultGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
var yourClassInstance = e.EditingElement.DataContext;
var editingTextBox = e.EditingElement as TextBox;
var newValue = editingTextBox.Text;
}
the "e" also contains information about Row and Column of the Cell. Thus you will know which editor the cell is using. In this case, i assume that it is a textbox.
Hope it help.

datagridview rowsremoved event gets called every time data loads

The datagridview rowsremoved event gets called every time the data gets loaded. It also makes sense to a certain extent that every time the data loads, the existing rows are removed. so technically the event should get called.
But how do i differenciate that from the actual delete button getting pressed. I don't think the key events should be used, that wouldn't be a clean approach.
Any help will be most appreciated.
You could use the UserDeletedRow event instead.
This is how we check to make sure the data has rows first:
If dataGridView.SelectedRows.Count > 0 Then
End If
See also: http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.userdeletingrow.aspx
You could have a private boolean variable to know when you are loading and when you are not.
private bool IsLoading { get; set; }
In the form Load event, you set the variable
private void MyForm_Load(object sender, EventArgs e)
{
this.IsLoading = true;
// do stuff
this.IsLoading = false;
}
private void DataGridView_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
if (this.IsLoading)
{
return;
}
//do stuff
}
Can you remove the event handler before loading the grid, and then re-add it afterward?

Passing variables from main form to input form

I have a simple question. I have a main form, and then a startup form from where I can select a new 3D model to generate. When selecting a new 3D model from the startup form, I want to check first whether the previous model I worked on has been saved or not. I simply want to pass a boolean value from the main form to the startup form using a delegate, but I can't seem to access the main form or any of its variables. I thought it would be as simple as saying: <code>frmMain myForm = new frmMain();</code>, but typing frmMain doesn't show up anything in intellisense.
Any hints?
Add a public property on your main form
public bool IsDirty
{
get;set;
}
you can then access this.ParentForm.IsDirty in your startup form,
remember to pass a reference to the main form when you show the startup form ... startupForm.showDialog(this);
Your main form is not accessible to Startup form.You have to store it to something that is accessible at a point where you want to use it.
You can do it by following way also ( along with other ways :)
// This class is mainly used to transfer values in between different components of the system
public class CCurrent
{
public static Boolean Saved = false;
}
make sure you put this class in namespace which is accessible to both the forms.
Now In your frmMain form set the value of CCurrent.Saved and access it in your startup form.
Here's my suggestion:
place a 3DModel object property in your main form:
private Model _model;
Declare your startup form as a Dialog ( like OpenFileDialog) and do something like this:
public void OpenModel()
{
using(var frm=new StartUpForm())
{
if(frm.ShowDialog()==DialogResult.OK))
{
if(_model.IsDirty)
{
if(MessageBox.Show("Model is changed do you want to save it?","",MessageBoxButtons.YesNo)==DialogResult.Yes)
_model.Save();
_model=frm.SelectedModel;
}
}
}
}
your startup form should have a interface like this:
public interface IStartupForm:IDisposable
{
DialogResult ShowDialog(IWin32Window parent);
Model SelectedModel{get;}
}

Resources