DataBinding in winforms : Update even though I click "Cancel" - c#

I have a problem with DataBinding in Winforms, Even though I click "Cancel" on the form, the objecte is updated.
I've set the property "DialogResult" of the Ok button to "OK", of the Cancel button to "Cancel", also, I've set the properties "AccesptButton" and "CancelButton" of the form to bnOk and bnCancel.
Here is my code :
Model :
private string code;
public string Code
{
get { return code; }
set { SetPropertyValue<string>("Code", ref code, value); }
}
private string libelle;
public string Libelle
{
get { return libelle; }
set { SetPropertyValue<string>("Libelle", ref libelle, value); }
}
UI :
public FamilleTiers CurrentFamilleTiers { get; set; }
private void FamilleTiersForm_Load(object sender, EventArgs e)
{
txCode.DataBindings.Add("Text", CurrentFamilleTiers, "Code");
txLibelle.DataBindings.Add("Text", CurrentFamilleTiers, "Libelle");
}
Edit function :
public static void EditFamilleTiers(FamilleTiers selectedFamilleTiers)
{
using (FamilleTiersForm form = new FamilleTiersForm() { CurrentFamilleTiers = selectedFamilleTiers, Text = selectedFamilleTiers.Libelle })
{
if (form.ShowDialog() == DialogResult.OK)
{
form.CurrentFamilleTiers.Save();
}
}
}
Thanks for your time

When you click cancel on a form data binding does not revert you need to keep a backup copy of the values and if they change replace the new values with the original values. .Net does not know what your wanting to do.

Related

C# WPF Keep dialog (child) window open if input is wrong

I am looking for a solution to not close my dialog window when there is a duplicate ID. I guess it has to do with DialogResult, however, I cannot make neither true or false work.
I am opening the "New Store"-window from a button click in my main window.
NewStoreWindow newStore = new NewStoreWindow();
newStore.ShowDialog();
As you can see on the screenshots below, you can fill in the ID and the Name. I have successfully made an input validation, so you cannot press "OK" when the 2 textboxes are empty.
When you press "OK" and the ID is already in my listview (gridview), it will give the user the below error message. However, it will also close the "New Store"-window. As said, I would like that the window is kept open until it successfully is added, so that the user can just edit the ID instead of having to open the "New Store"-window again and typing it again.
It should only close the window when it is successfully added or cancel/X is pressed.
I have tried to play around with closing-event for "New Store", but it does not seem to work.
Is this just the designed behavior? And is there a way to bypass/work around it?
CS
public partial class NewStoreWindow : Window
{
public static bool itemAlreadyAdded;
//public MainWindow mainWin = new MainWindow();
public bool IsDefault { get; set; }
public NewStoreWindow()
{
InitializeComponent();
//SetButtonState();
}
// Data Binding
public static readonly DependencyProperty SidProperty = DependencyProperty.Register("Sid", typeof(string), typeof(NewStoreWindow), new UIPropertyMetadata(String.Empty));
public string Sid
{
get { return (string)GetValue(SidProperty); }
set { SetValue(SidProperty, value); }
}
// Data Binding
public static readonly DependencyProperty SNameProperty = DependencyProperty.Register("SName", typeof(string), typeof(NewStoreWindow), new UIPropertyMetadata(String.Empty));
public string SName
{
get { return (string)GetValue(SNameProperty); }
set { SetValue(SNameProperty, value); }
}
private void cmdOK_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
MainWindow mainWin = new MainWindow();
// Check if any items exist in listview
if (MainWindow._list.Count == 0)
{
MainWindow._list.Add(new MainWindow.Data() { Sid = Sid, SName = SName });
}
else // items exist
{
itemAlreadyAdded = false; // use for boolean checking
foreach (var item in mainWin.lvStores.Items.OfType<MainWindow.Data>()) // loop through all items in listview
{
if (item.Sid == Sid) // Check if new item already exists
{
itemAlreadyAdded = true;
}
if (itemAlreadyAdded) // Show messagebox if it exists
{
MessageBox.Show("ID needs to be unique, please respecify!", "Duplicate ID",
MessageBoxButton.OK, MessageBoxImage.Error);
break;
}
}
if (!itemAlreadyAdded) // If it does not already exist, add it
{
// if it does not exist, create it from the textbox values
MainWindow._list.Add(new MainWindow.Data() { Sid = Sid, SName = SName });
// Refresh listview
mainWin.lvStores.Items.Refresh();
// Close Window
Close();
}
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
}
public void Close()
{
this.Closing -= Window_Closing;
base.Close();
}

Object loses data after initialization

I have these objects in my project:
SchedulerList
SchedulerListItem
SchedulerListItemDetails
each one is a win forms control, which are used in forms of my application. The SchedulerList holds SchedulerListItems and each item can have SchedulerListItemDetails.
my code goes as follows:
//creating my initial list form
FrmListTesting f = new FrmListTesting();
f.Show();
The form has only one button that has a hard-coded parameter for testing purposes, as well as a SchedulerList control taht will hold the list items.
When the button is clicked the form does the following:
private void button1_Click(object sender, EventArgs e)
{
var control = this.Controls[1] as SchedulerList;
var path = #"D:\Share\Countries.txt";
var sli = new SchedulerListItem(path);
control.AddItem(sli);
}
my SchedulerListItem constuctor goes as follows:
public SchedulerListItem(string path)
{
InitializeComponent();
this.Name = Path.GetFileNameWithoutExtension(path);
this.SourcePath = path;
this.DestinationPath = GetDestinationPath(path);
}
And the AddItem method is defined as:
public void AddItem(SchedulerListItem item)
{
this.flPanel.Controls.Add(item);
}
The add item method works as intended, displays all the data that was required and displays it in the UI. The list item has a button that brings up the details form as such:
//the form constructor
public FrmSchedulerItemDetails(SchedulerListItem item)
{
InitializeComponent();
this.detailsControl = new SchedulerListItemDetails(item, this);
}
//control constructor
public SchedulerListItemDetails(SchedulerListItem item, Form owner)
{
InitializeComponent();
this.SourcePath = item.SourcePath;
this.DestinationPath = item.DestinationPath;
this.OldFormat = item.OldFormat;
this.ExportToExcel = item.ExportToExcel;
this.owner = owner;
this.underlyingItem = item;
}
And now the problem. After the SchedulerListItemDetails constructor is called and the data "gets initialized", when i look at the data inside the object its set to default values. it seams that everything that I set after InitializeComponent(); gets ignored.
things that i have tried:
hard-coding the values to see if primitives get passed correctly
settings breakpoints on every InitializeComponent() method to see the stack trace associated with setting to default values
none of the methods show any results... I know that if i use a form directly instead of using a control within a from i can set the values the way i want to, but I'm very confused as to why this other method with controls doesn't work.
EDIT 1:
the code for SchedulerListItemDetails:
public partial class SchedulerListItemDetails : UserControl
{
public SchedulerListItemDetails(SchedulerListItem item, Form owner)
{
InitializeComponent();
this.SourcePath = item.SourcePath;
this.DestinationPath = item.DestinationPath;
this.OldFormat = item.OldFormat;
this.ExportToExcel = item.ExportToExcel;
this.owner = owner;
this.underlyingItem = item;
}
public SchedulerListItemDetails()
{
InitializeComponent();
}
private Form owner = null;
private SchedulerListItem underlyingItem;
public Boolean ExportToExcel
{
get
{
return this.cbxExcel.Checked;
}
set
{
this.cbxExcel.Checked = value;
}
}
public Boolean OldFormat
{
get
{
return this.cbxOldFormat.Checked;
}
set
{
this.cbxOldFormat.Checked = value;
}
}
public String DestinationPath
{
get
{
return this.tbxDestinationPath.Text;
}
set
{
this.tbxDestinationPath.Text = value;
}
}
public String SourcePath
{
get
{
return this.tbxSourcePath.Text;
}
set
{
this.tbxSourcePath.Text = value;
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
this.owner.Close();
}
private void btnSave_Click(object sender, EventArgs e)
{
underlyingItem.SourcePath = this.SourcePath;
underlyingItem.DestinationPath = this.DestinationPath;
underlyingItem.OldFormat = this.OldFormat;
underlyingItem.ExportToExcel = this.ExportToExcel;
btnCancel_Click(sender, e);
}
}
I'll make an answer, because it should help you to solve your problem.
You have default (parameterless) constructor, which may be called and if it is called, then your constructor with parameters is not called.
Proper design would be something like
public partial class SchedulerListItemDetails : UserControl
{
public SchedulerListItemDetails()
{
InitializeComponent();
}
public SchedulerListItemDetails(SchedulerListItem item, Form owner): this()
{
this.SourcePath = item.SourcePath;
...
}
}
Notice this(), this ensure what parameterless constructor is called before (and InitializeComponent() as well, no need to duplicate it in another constructor).
Back to your problem. In your case it's like this
public partial class SchedulerListItemDetails : UserControl
{
public SchedulerListItemDetails()
{
InitializeComponent();
}
public SchedulerListItemDetails(SchedulerListItem item, Form owner)
{
InitializeComponent();
this.SourcePath = item.SourcePath;
...
}
}
Only one constructor can be called. So if you put breakpoint in parameterless one and it's triggered, then you have problems. Because you create somewhere SchedulerListItemDetails without setting it's properties (they stay default).
More likely problem is that you create new instance of that object (either before or after constructing proper, if your code ever construct such object) and that instance is what you inspect later.
So after i got a quick course of how win forms work i figured out what the problem was.
my code that i thought was enough is:
public FrmSchedulerItemDetails(SchedulerListItem item)
{
InitializeComponent();
this.DetailsControl = new SchedulerListItemDetails(item, this);
}
public SchedulerListItemDetails DetailsControl
{
get
{
return this.detailsControl;
}
set
{
this.detailsControl = value;
}
}
the this.detailsControl is the control im trying to setup, but as i have learned the correct way of replacing a component for a new one is:
public FrmSchedulerItemDetails(SchedulerListItem item)
{
InitializeComponent();
this.DetailsControl = new SchedulerListItemDetails(item, this);
}
public SchedulerListItemDetails DetailsControl
{
get
{
return this.detailsControl;
}
set
{
this.Controls.Remove(this.detailsControl);
this.detailsControl = value;
this.Controls.Add(this.detailsControl);
}
}
Feel kinda silly now :).

Properties are 'reset' with two forms?

I'm using MainWindow and Settings. MainWindow is the startup window from which I can open Settings. I'm trying to share some properties between both windows. Right now, I have the public properties declared in Settings:
public partial class Settings : Form
{
private string property1
public Settings()
{
InitializeComponent();
this.changeSettings();
}
public string property1
{
get { return property1; }
set { property1 = value; }
}
public void changeSettings()
{
textbox.Text = property1;
}
}
I can create an instance of Settings in MainWindow and change the properties from there:
public partial class Mainwindow : Form
{
private Settings settings;
public MainWindow()
{
InitializeComponent();
settings = new Settings();
this.changeSettings();
}
private void changeSettings()
{
settings.property1 = "value";
textbox.Text = settings.property1;
}
private void openSettings_Click(object sender, EventArgs e)
{
settings.ShowDialog();
}
}
Say, I want to change the contents of the textboxes in both forms. For MainWindow this works, i.e. I can store the value in the property and access it again. However, I open up Settings and try to change its textbox, the property is empty!
What could explain this?
You never called changeSettings() after setting the property.
You should probably get rid of that method and update the textbox directly in the setter.
The flaw is in Settings.property1, it doesn't update the text box that displays its value. A simple solution is:
public string property1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
You'll also need to update your MainWindow's text box after displaying the dialog:
private void openSettings_Click(object sender, EventArgs e)
{
settings.property1 = textbox.Text;
if (settings.ShowDialog() == DialogResult.OK) {
textbox.Text = settings.property1;
}
}

DataGridView with ContextMenu assigned to column and a MessageBox

I have a DGV that has its datasource set to a BindingList. There is also a ContextMenu assigned to a column in the DGV. There is a MenuItem set on the ContextMenu that calls a MessageBox on the click event.
Everything works fine and the Methods are called and the MessageBox with YesNo responses do what they are susppose to.
The problem that I am having is that when the MessageBox's click event occurs (Yes or No) it does it's job and goes away. If the same routine is called a second time, it again does it's job with no problem, then reappears. If I click Yes or No it goes away. If I call it a third time the MessageBox appears again does its job and reappears twice. As if everytime it's being called its iterating and calling itself again that amount of times. This will occur for everytime it's called.
The BindingList is built using a Class with nested properties and all data elements are present.
I tried using just a blank MessageBox with no DialogResults and no change. I even tried using the DGV's RaiseListChangedEvents=false in the ContextMenu click event and the DGV's Cell Enter Click Event.
I've stepped through my code and and no matter what the Class with the nested properties always gets called and causes the ContextMenu's click event to be called again and again... I figure this is by design since a BindingList will always AutoUpdate when a cell's value is accessed or changed.
ContextMenu's Column is a Button and is readonly.
So how do I either catch the MessageBox after it's run the first time or stop the BindingList from auto updating. My List draws its data from a Web Reference and I handle updates through the methods provided from the API. The only reason I'm using a BindingList is because the DGV doesn't work with just a List .
Thank you for any help or guidance. (First time posting, but have gathered and used a lot of info from here)
Here's some code:
_requestsView.AutoGenerateColumns = false;
_edit.DataPropertyName = "RequestId";
_patient.DataPropertyName = "Patient";
_dateSubmitted.DataPropertyName = "Date";
_completedBy.DataPropertyName = "CompletedBy";
_completedOn.DataPropertyName = "CompletedOn";
_procedure.DataPropertyName = "Procedure";
_stat.DataPropertyName = "Stat";
_viewReport.DataPropertyName = "ViewReport";
_selectedSpecialist.DataPropertyName = "SelectedSpecialist";
_status.DataPropertyName = "Status";
_rate.DataPropertyName = "Rating";
_requestsView.DataSource = _requestsBinding;
// _cancelRequest_Click is ContextMenu MenuItem
void _cancelRequest_Click(object sender, EventArgs e)
{
MessageBox.Show("test");
}
private void _requestsView_CellEnter(object sender, DataGridViewCellEventArgs e)
{
if (_requestsView.CurrentRow != null)
if (_requestsView.CurrentRow.Cells["_viewReport"].Selected)
try
{
var requestNumber = (int)_requestsView.CurrentRow.Cells ["_viewReport"].Value;
var letter = Api.Client.getCompletedLetter(UseSession.SessionId, requestNumber);
var convertedLetter = Convert.FromBase64String(letter);
var requestNumberToString = Convert.ToString(requestNumber);
var tmpfile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), requestNumberToString + #".pdf");
var view = new ViewLetter(requestNumberToString, tmpfile);
File.WriteAllBytes(tmpfile, convertedLetter);
view._pdf.LoadFile(tmpfile);
view._pdf.PerformLayout();
view._pdf.Refresh();
view._pdf.setShowToolbar(true);
view._pdf.setZoom(100);
view.Show();
view.Activate();
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
if (_requestsView.CurrentRow != null)
if (_requestsView.CurrentRow.Cells["_edit"].Selected)
_edit.ContextMenuStrip.Show(Cursor.Position.X, Cursor.Position.Y);
if (_requestsView.CurrentRow != null)
if (_requestsView.CurrentRow.Cells["_rate"].Selected)
_rate.ContextMenuStrip.Show(Cursor.Position.X, Cursor.Position.Y);
}
public class Requests
{
private int _requestId;
private DateTime _date;
private string _patient;
private string _completedBy;
private string _completedOn;
private string _procedure;
private string _stat;
private int _viewReport;
private Specialists _selectedSpecialist;
private string _status;
private int _rating;
public Requests()
{ }
public Requests(string stat)
{
_stat = stat;
}
public int RequestId
{
get { return _requestId; }
set { _requestId = value; }
}
public DateTime Date
{
get { return _date; }
set { _date = value; }
}
public string Patient
{
get { return _patient; }
set { _patient = value; }
}
public string CompletedBy
{
get { return _completedBy; }
set { _completedBy = value; }
}
public string CompletedOn
{
get { return _completedOn; }
set { _completedOn = value; }
}
public string Procedure
{
get { return _procedure; }
set { _procedure = value; }
}
public string Stat
{
get { return _stat; }
set { _stat = value; }
}
public int ViewReport
{
get { return _viewReport; }
set { _viewReport = value; }
}
public Specialists SelectedSpecialist
{
get { return _selectedSpecialist; }
set { _selectedSpecialist = value; }
}
public string Status
{
get { return _status; }
set { _status = value; }
}
public int Rating
{
get { return _rating; }
set { _rating = value; }
}
}
Just wanted to update this and close it. I figured out a work around that sets a boolean true or false during different stages of events being called. If the boolean is set to true I just do a return to get out of the methods.

GridView FocusedRowChanged - Child Class object

I need some help here.
I've created a child class called MyEditorRow from DevExpress EditorRow, and added 3 properties
public class myEditorRow : EditorRow
{
public myEditorRow()
{
}
private string inRowDescription = null;
public string RowDescription
{
get { return inRowDescription; }
set { inRowDescription = value; }
}
private bool inRequired = false;
public bool Required
{
get { return inRequired; }
set { inRequired = value; }
}
private bool inInherits = false;
public bool Inherits
{
get { return inInherits; }
set { inInherits = value; }
}
Second part of the code somewhere in the program adds instance of MyEditorRow to DevExpress VGrid Control.
vgcGrid.Rows.Add(Row);
My question is this: How can I link MyEditorRow class with DevExpress VGrid Control FocusedRowChanged event, so I can get my custom properties when row focus changes.
Thanks
The e.Row parameter is of the BaseRow type. So, to obtain an instance of the MyEditorRow object in the FocusnedRowChanged event handler, use the following code:
private void vGridControl1_FocusedRowChanged(object sender, DevExpress.XtraVerticalGrid.Events.FocusedRowChangedEventArgs e) {
if(e.Row is myEditorRow) {
myEditorRow row = ((myEditorRow)e.Row);
// your code here
}
}

Categories