Working on a project where I am storing my data using a GenericList, based off a class I created which contains a handful of string members. This works great, but things get troublesome when I try to pass this list to a second form. I initially setup a get/set block in the second form to accept the list and then return it, but the destination is always null. As a workaround I changed the local list on the second form to public and am able to access it that way, but then instead of getting a copy of the list, I get ByRef passing so any changes made are reflected in the master list. Since I want to be able to not accept these changes, I really want this to be a pass by value. These two problems have to be linked...
Here is the setup. In form1 I have this definition at the class level, making this somewhat of a global variable (yeah its bad, but it works for me):
private List<ServerList> theServerList = new List<ServerList>();
Later on I create the new form and (try to) pass my data into it using:
frmEditor theEditor = new frmEditor();
theEditor.updatedServerList = theServerList;
DialogResult res = theEditor.ShowDialog();
On the second from, I have this to receive the data:
private List<ServerList> myServerList = new List<ServerList>();
public List<ServerList> updatedServerList
{
get { return myServerList; }
set { myServerList = updatedServerList; }
}
This results a list on form2 (the myServerList) always being empty. Since this was not working, I commented out all these lines and changed the myServerList definition on form2 to public. Now after instantiating form2 I am able to get the data over by doing this:
theEditor.myServerList = theServerList;
This works in that the data nicely shows up form2, but this kind of an assignment just copies the pointer of the data block in memory from one variable to the other (basically a ByRef passing), so any changes I make on form2 change "both" lists since they are the same. I would like to have a local copy in form2 so I can make changes and then accept them or not depending of if the user clicks Cancel (drop all changes), or OK (copy the local data from form2 back to form1).
Your original does not work because your setter should be:
public List<ServerList> updatedServerList
{
get { return myServerList; }
set { myServerList = value; }
}
If you want to copy the list, you can use ToList on the incoming value:
public List<ServerList> updatedServerList
{
get { return myServerList; }
set { myServerList = value.ToList(); }
}
This is not to do with 'by value' vs 'by reference' since List<T> is a reference type, so the value of a variable of type List<ServerList> is a reference which is copied into the setter method. This reference will point to the same object in both the caller and receiver classes. If you want to copy the contents of the list you need to do it manually using ToList or similar.
I am trying to bind data from several datagrids and I don't want to view all of them every time I start app.
Is there any possibility to set it in settings? Or automaticaly set all datagrids as viewed?
Thanks
Maybe your thinking about this the wrong way? If you want your data before the table is shown what you need to do is get and store the data in a DataTable and then use that to play with. Then when it comes to displaying your DataGridView just simply bind the DataTable to it and display it. I have included a sample below. But if its just a case of showing the tables then either tablename.visable = false should do the trick or alternitivly if you have other elements such as a panel you could use panelname.BringToFront or tablename.SendToBack.
Lets begin the OOP example..
This you could have in another interface class where you want to manipulate the data. In this example this class would be IOptions, here you could have all the data tables you need for Options for example. You could have different Interface Classes representing certain parts of your program, but we will stick with options for now..
public DataTable mProgramOptionsList;
Then when the program starts (your main page / main.cs) you attach IOptions to any of the classes that need at this DataTable.
mIOptions = new IOptions();
mPresenterOptions = new PresenterOptions();
mPresenterOptions.AttachInterface(mIOptions);
mModelOptions = new ModelOptions();
mModelOptions.AttachInterface(mIOptions);
Example code in PresenterOptions()
IOptions mIOptions;
public void AttachInterface(IOptions pOptions)
{
mIOptions = pOptions;
}
Now in Presenter Options you can modify the tables as you please, as well as fill them..
mIOptions.mProgramOptionsList = mModelOptions.GetProgOptionsFromDB();
This is the class you want to have your table control in. This could be a separate GUI/View class with its own layout and structure. As long as you have attached the Interface (IOptions) to the class your using to hold all your tables.
private IOptions mIOptions;
private BindingSource mbsProgramOptions;
public int mOptionsID { get; set; }
void AttachOptionsInterface(IOptions pOptions)
{
mIOptions = pOptions;
CreateDataBindings();
}
private void CreateDataBindings()
{
mbsProgramOptions= new BindingSource();
mbsProgramOptions.DataSource = mIOptions.mProgramOptionsList; // Or what ever datatable you have made
dgvProgramOptions.AutoGenerateColumns = false;
dgvProgramOptions.DataSource = mbsProgramOptions; // this binds your DataGridView to the DataSource
}
Once this is done not only can you add or change the data to any class you have attached IOptions too you can also display the table anywhere you please by just doing something like (Assuming you called your table control class above TableHelper):
In the PresenterOptions or any class for that matter:
TableHelper ProgramOptionsList;
ProgramOptionsList.mOptionsID = 1234; // Accessing a public/get;set; variable in TableHelper Class
ProgramOptionsList.Text = "Table Title";
ProgramOptionsList.ShowDialog();
ProgramOptionsList.BringToFront(); // Just in case you have multiple tables over lapping each other in TableHelper
I have been working with Entity Framework (VS2010 Framework 4.0) in my proyect. I had some trouble with using a different object context per form. What I did then, was to create a object context in the Main Menu Form (stays opened) and everytime I create and show one form, I pass that object context to this new form. Example:
public partial class frm_Menu : Base
{
public Sistema_financiero_Entities db = new Sistema_financiero_Entities();
private void cancelarCuotaToolStripMenuItem_Click(object sender, EventArgs e)
{
frm_Cancelacion_Cuota Form1 = new frm_Cancelacion_Cuota();
Form1.db = db;
Form1.Show();
}
}
Ok, that solution worked fine until now because I needed to use and pass objects throw the differents forms sometimes, and if the objects contexts were different, I got an error.
Now, I have detected a huge issue using it this way. I have a form, where I can pay for the different installments of a loan. I´ll attach an image so then you can see what I´m talking about.
There, you select the different installments you want to pay for. Then, you introduce the value you will finally pay in "Total cobrado". Here is the important thing: When the checkbox image is checked (the blue one - already checked in the image), I create a "payment" entity per installment. Every "payment" object is stored in a list. If I uncheck it, I can change the value and the same thing is done. Obviously, I´m clearing the list before doing a list.Clear();. Then, one the checkbox checked, I can press "Aceptar" (accept). There I add to the database every "payment"(PAGO) in the list. After that, I save all changes.
foreach (Pago p in Lista_nuevos_pagos)
{
db.AddToPago(p);
}
try
{
db.SaveChanges();
this.Close();
}
My problem, is that it´s not only adding those "payments" in the list but the other "payments" entities that were in the list before clearing it. I reach the conclusion that when I clear the list, the objects remains in the object context. I thought that if the entity is not in the database, I have to Add it to the entity in the object context as I did with pago (db.AddToPago(p);).
I wanted to ask you guys how can I solve this issues. I solved it now doing this:
private void cancelarCuotaToolStripMenuItem_Click(object sender, EventArgs e)
{
Sistema_financiero_Entities db = new Sistema_financiero_Entities();
frm_Cancelacion_Cuota Form1 = new frm_Cancelacion_Cuota();
Form1.db = db;
Form1.Show();
}
Instead of creating just one global db for all forms, I create one in the Main Menu for every form. Then, in that form closed event, I dispose that object context.
Then, when i check the checkbox image, before creating the "payments", I delete every "Pago" entity from the object context:
foreach (Pago p in Lista_nuevos_pagos)
{
db.DeleteObject(p);
}
Lista_nuevos_pagos.Clear();
Doing this works correctly, but I´m still having trouble with some other created entities (Installments) that are not deleted when I clear a list. I think I´m doing it wrongly, thats why I need some direction to use EF correctly. I really need to get this done really soon, I don´t have too much time to read EF tutorials.
Just in case, this is how I create every "Pago" (payment)
Pago p = new Pago();
p.desc_aumento_intereses = nudwb1.Value;
p.desc_aumento_punitorios = nudwb2.Value;
p.desc_aumento_gastos = nudwb3.Value;
p.desc_aumento_comision = nudwb4.Value;
p.cotizacion = ntxt_Cotizacion.Value;
p.fecha_hora = fecha_hora;
Cuota c = new Cuota();
string name = tbx.Name.Substring(tbx.Name.IndexOf("-") + 1);
int nro_cuota = Convert.ToInt32(name);
c = Lista_cuotas_cobrar.Where(x => x.num_cuota == nro_cuota).First();
p.Cuota.Add(c);
Thank you for reading, I know this is a lot of info. Hope some guide soon..
I guess that you have references to those object in your Lista_nuevos_pagos list. This is why they will be duplicated.
I have a list of Strings.
Well, conceptually. They are stored somewhere else, but I want provide an object which acts like a list (and provides any necessary events on top of that), with properties that I could bind to.
I want to establish a two-way binding over this data, to display it as a modifiable column in a DataGrid. I have the following problems with that:
I can't make a two-way binding because the binding needs a path (i.e. I can't have it look like {Binding} or {Binding Path=.} in the column, must be {Binding Path=someField"} to be made modifiable if I got this right, which sounds reasonable).
I don't exactly know how the proxy collection object should look like, in terms of interfaces (would IEnumerable + INotifyCollectionChanged sufficient?)
Is there any solution which doesn't involve creating one proxy object per every String in the collection? Could you suggest an efficient design?
To keep the discussion on the rails, let's assume I want to bind to something like this:
class Source {
public String getRow(int n);
public void setRow(int n, String s);
public int getCount();
public void addRow(int position, String s);
public void removeRow(int position);
}
That's not exactly my case, but when I know how to bind to this, I think I'll be able to handle any situation like this.
I'm OK with having to provide an adapter object on top of that Source, with any necessary interfaces and events, but I don't want to have one adapter object per row of data.
While making an adapter for the Source is relatively clear, then, unfortunatelly, the core of the second problem ('not wrapping every string in a miniobject') is a clash built into the .Net and WPF..
The first thing is that the WPF does provide you with many ways of registering 'on data modified' callbacks, but provides no way of registering callbacks that would provide a value. I mean, the "set" phase is only extendable, not interceptable, and the "get" - nothing at all. WPF will simply keep and return whatever data it has once cached.
The second thing is that in .Net the string is ... immutable.
Now, if ever you provide a string directly as a pathless binding or as a datacontext to any control, you are screwed in a dead end. The problem is, that WPF actually passes only the actual value of the binding, without the information of "where it came from". The underlying control will be simply given the string instance, and will have no sane way of modifying it as the string cannot change itself. You will not be even notified about such attempt, just like with read-only properties. What's more - if you ever manage to intercept such a modification attempt, and if you produce a proper new string, the WPF will never ask you again for the new value. To update the UI, you'd have to mannually, literally, force the WPF to re-ask you by for example changing the original binding so it points elsewhere (to the new value) or set the datacontext (to the new instance). It is doable with some VisualTree scanning, as every 'changed' callback gives you the DependencyObjects (Controls!), so yo ucan scan upwards/downwards and tamper with their properties.. Remember that option - I'll refer to this in a minute.
So, everything boils down to the fact that to get a normal 2-way binding you do not have to have a Path, you "just" have to have a mutable underlying data object. If you have immutable one - then you have to use a binding to a mutable property that holds the immutable value..
Having said that, you simply have to wrap the strings some how if you want to modify them.
The other question is, how to do that. There's a plenty of ways to do it. Of course, you can simply wrap them like Joe and Davio suggested (note to Joe: INotify would be needed there also), or you can try to do some XAML tricks with attached properties and/or behaviours and/or converters to do that for you. This is completely doable, see for example my other post - I've shown there how to "inject a virtual property" that pulled the data completely from elsewhere (one binding+converter performed the wrapping on the fly, second binding extracted the values from the attached-wrapper). This way you could create a "Contents" property on the string, and that property could simply return the string itself, and it'd be completely 2-way bindable with no exceptions.
But.. it would NOT work 2-way-ish.
Somewhere at the root of your binding/behaviour/conveter chain, there will be an immutable string. Once your smart autowrapping binding chain fires with 'on modified' callback you will be notified with pair of old/new values. You will be able to remap the values to new and old strings. If you implemented everything perfectly, the WPF will simply use the new value. If you tripped somewhere, then you will have to push the new value artificially back to the UI (see the options I'd asked you to remember). So, it's ok. No wrapper, old value was visible, it was changeable, you've got new value, the UI displays new value. How about storage?
Somewhere in the meantime you've been given a old/new value pair. If you analyze them, you'll get old/new strings. But how do you update the old immutable string? Can't do. Even if autowrapping worked, even if UI worked, even if editing seemed to work, you are now standing with the real task: you onmodified callback was invoked and you have to actually update that immutable string piece.
First, you need your Source. Is it static? Phew. What a luck! So surely it is instanced. In the on-modified callback we got only a old+new string.. how to get the Source instance? Options:
scan the VisualTree and search for it in the datacontexts and use whatever was found..
add some more attached properties and binding to bind a virtual "Source" property to every string and read that property from the new value
Well doable, but smells, but no other options.
Wait, there's more: not only the old/new value and an instance of Source are needed! You also need the ROW INDEX. D'oh! how to get that from the bound data? Again, options:
scan the VisualTree and search for it (blaargh)...
add some more attached properties and bindings to bind a virtual "RowIndex" property to every (blaaergh)...
At this point of time, while I see that all of this seems implementable and actually might be working properly, I really think that wrapping each string in a small
public class LocalItem // + INotifyPropertyChanged
{
public int Index { get; }
public Source Source { get; }
public string Content
{
get { Source...}
set { Source... }
}
}
will simply be more readable, elegant and .. SHORTER to implement. And less error-prone, as more details will be explicit instead of some WPF's binding+attached magic..
I find your approach a little weird.
DataGrids are usually used to display Rows. Rows consist of data that belongs together.
You could for instance easily map a row to a certain class. This means that the columns in your datagrid represent properties in your class.
What you're trying to do is the opposite, you're trying to get a relation between the column values instead of the row values.
Wouldn't it be easier to have a collection of your class which you can then bound the column to?
For instance
class MyClass : INotifyPropertyChanged
{
// Remember to actually implement INotifyPropertyChanged
string Column;
}
If you would have an ObservableCollection of MyClass you could bind the DataGrid to this collection. Whenever the property which I called "Column" changes, you could update your special list.
You can do this by hooking up some events. With the implementation of INotifyPropertyChanged, your columns will be updated if you update the "Column"-value directly.
I have this bit of code I use to bind a list of custom object to a DataContextMenu. You can alter it to use a list of strings and bind it to what you need
class SampleCode
{
class Team
{
private string _TeamName = "";
private int _TeamProperty1 = 0;
ObservableCollection<Territory> _Territories = new ObservableCollection<Territory>();
public Team(string tName)
{
this.TeamName = tName;
}
public ObservableCollection<Territory> Territories
{
get { return _Territories; }
set { _Territories = value; }
}
public string TeamName
{
get { return _TeamName; }
set { _TeamName = value; }
}
public int TeamProperty1
{
get { return _TeamProperty1; }
set { _TeamProperty1 = value; }
}
}
class Territory
{
private string _TerritoryName = "";
Team _AssociatedTeam = null;
public Territory(string tName, Team team)
{
this.TerritoryName = tName;
this.AssociatedTeam = team;
}
public Team AssociatedTeam
{
get { return _AssociatedTeam; }
set { _AssociatedTeam = value; }
}
public string TerritoryName
{
get { return _TerritoryName; }
set { _TerritoryName = value; }
}
public void Method1()
{
//Do Some Work
}
}
class MyApplication
{
ObservableCollection<Team> _Teams = new ObservableCollection<Team>();
ContextMenu _TeritorySwitcher = new ContextMenu();
public MyApplication()
{
}
public void AddTeam()
{
_Teams.Add(new Team("1"));
_Teams.Add(new Team("2"));
_Teams.Add(new Team("3"));
_Teams.Add(new Team("4"));
foreach (Team t in _Teams)
{
t.Territories.Add(new Territory("1", t));
t.Territories.Add(new Territory("2", t));
t.Territories.Add(new Territory("3", t));
}
SetContextMenu();
}
private void SetContextMenu()
{
HierarchicalDataTemplate _hdtTerritories = new HierarchicalDataTemplate();
_hdtTerritories.DataType = typeof(Territory);
HierarchicalDataTemplate _hdtTeams = new HierarchicalDataTemplate();
_hdtTeams.DataType = typeof(Team);
FrameworkElementFactory _TeamFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TeamFactory.Name = "txtTeamInfo";
_TeamFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TeamProperty1"));
FrameworkElementFactory _TerritoryFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TerritoryFactory.Name = "txtTerritoryInfo";
_TerritoryFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TerritoryProperty1"));
_hdtTeams.ItemsSource = new Binding("Territories");
_hdtTeams.VisualTree = _TeamFactory;
_hdtTerritories.VisualTree = _TerritoryFactory;
_hdtTeams.ItemTemplate = _hdtTerritories;
_TeritorySwitcher.ItemTemplate = _hdtTeams;
_TeritorySwitcher.ItemsSource = this._Teams;
}
}
}
Lazy solution
Derive from ObservableCollection<string> and let that collection be populated from the Source. In the derived class, register to collection change events and update the source accordingly. Bind the DataGrid column to the observable collection.
This should be pretty straightforward to write, but has a big drawback of duplicating all data in the collection.
More efficient solution
Create an adapter (as you suggested) and implement IList<string> and INotifyCollectionChanged. Let the list operations fall through directly to the source. Bind the DataGrid column to the adapter.
This approach would require some tedious boilerplate, but it's a thin layer between the WPF control and your Source.
This really depends on how you're implementing the UI. Bea Stollnitz did an excellent post of virtualizing the ItemsSource for the WPF DataGrid at http://bea.stollnitz.com/blog/?p=344 . With work I used this to edit as well as display data.
The easiest way is by placing the string in a wrapper class.
public class Wrapper
{
public string Content{get;set;}
}
Then you use the string via the wrapper class. This was the list items remain the same but the content changes.
The problem is when you do this without that then an old string is being deleted and a new one is created and the collection is confused.
Start with an ObservableCollection<string>. Then set the bindable control's ItemsSource to the ObservableCollection.
As a course project i'm building a form in c# which contains two user controls.
The first user control has a checkedlistbox and the second control has also a checkedlistbox when the first control checkedlistbox will contain list of people (male/female) and the second user control the checkedlistbox will have two options: male, female and when I click a button on the first control which says: "update friends" it's suppose to go to the second control and check if we selected male or female and according to that to update the checkedlistbox in the first user control with friends by gender type by what was selected on the second control.
Basically I want to raise an event every time the button on the first control selected then to get the data from the second control to the first control.
Is it possible to do so between two controls who are inside a form and are different controls?
Any help will be appriciated.
Thanks.
To do this "correctly," you would want to use something like the MVC architecture. It's definitely a lot more work initially to understand and implement but is very useful to know if you plan on doing any serious UI application development. Even if you don't go all the way with it, the concepts are useful to help design even "quick and dirty" applications.
Define your data model without thinking in terms of the UI, e.g.:
internal enum Gender
{
Male,
Female
}
internal class Person
{
public Gender Gender { get; set; }
public string Name { get; set; }
}
// . . .
// Populate the list of people
List<Person> allPeople = new List<Person>();
allPeople.Add(new Person() { Gender = Gender.Male, Name = "Xxx Yyy" });
allPeople.Add(new Person() { Gender = Gender.Female, Name = "Www Zzz" });
// . . .
For the view portion, you would typically use data binding on the UI controls so that the controls will automically reflect changes to the underlying data. However, this can get difficult especially if you are not using a database-like model (e.g. System.Data.DataSet). You may opt to "manually" update the data in the controls which might be fine in a small app.
The controller is the portion that uses the UI events and makes changes to the model, which may then be reflected as changes in the view.
internal class Controller
{
private Gender selectedGender;
private List<Person> allPeople;
private List<Person> friends;
public Controller(IEnumerable<Person> allPeople)
{
this.allPeople = new List<Person>(allPeople);
this.friends = new List<Person>();
}
public void BindData(/* control here */)
{
// Code would go here to set up the data binding between
// the friends list and the list box control
}
// Event subscriber for CheckedListBox.SelectedIndexChanged
public void OnGenderSelected(object sender, EventArgs e)
{
CheckedListBox listBox = (CheckedListBox)sender;
this.selectedGender = /* get selected gender from list box here */;
}
// Event subscriber for Button.Click
public void OnUpdateFriends(object sender, EventArgs e)
{
this.friends.AddRange(
from p in this.allPeople
where p.Gender == this.selectedGender
select p);
// If you use data binding, you would need to ensure a
// data update event is raised to inform the control
// that it needs to update its view.
}
}
// . . .
// On initialization, you'll need to set up the event handlers, etc.
updateFriendsButton.Click += controller.OnUpdateFriends;
genderCheckedListBox.SelectedIndexChanged += controller.OnGenderSelected;
controller.BindData(friendsListBox);
// . . .
Basically, I recommend not having controls talk directly, but rather through a controller-like class as above which has knowledge of the data model and the other controls in the view.
Of course it's possible: you need to make the link between the 2 controls in the form.
Just declare an event 'ButtonClicked' in control #1
Then make a public method 'PerformsClick' on the control #2
And in the form, in the constructor, after the call to InitializeComponent, link the event from the control #1 to the method to the control #2:
control1.ButtonClicked += delegate(sender, e) {
control2.PerformsClick();
};
(I type on the fly to give you an idea, it'll surely not compile)
If you want to pass any data, just add parameters in the PerformsClick method.