My program class has:
Application.Run(new Form1());
in form1 class I have:
model = new Model(this);
private void userEnteredText()
{
Model clonedModel = (Model)model.Clone();
this.myButton.Size = new System.Drawing.Size(10,5);
MessageBox.Show("buttons made small");
this = clonedModel;
MessageBox.Show("clone complete and buttons restored to orig size");
}
in model class I have:
public Model(Form1 form1)
{
myform = form1;
}
public object Clone()
{
return new Model(myform);
}
My initial form1 object has the size of buttons really large. After the user enters a value in a textbox: I clone the model object and call a method that makes the buttons really small. How can I then set the model object to point back to the original model object with large buttons?
I'm getting this error:
"Cannot assign to this because it is read-only"
I know I can just change the button size but I need to clone the entire object because there are other original variables that I want reset.
One question - is it expected behaviour that whilst clone both models references the same Form? Clone() method just creates a new instance of Model but it still reference the same Form object,
You can persist state of the initial model in an other private field like
private Model backupModel;
and before applying a user-defined values just backup and later restore current model like:
private void userEnteredText()
{
this.backupModel = model;
Model clonedModel = (Model)model.Clone();
this.myButton.Size = new System.Drawing.Size(10,5);
MessageBox.Show("buttons made small");
model = this.backupModel;
MessageBox.Show("clone complete and buttons restored to orig size");
}
Related
I would like to know how can I create an array of Circular Progress Bars and access its properties within the array, like CircularProgressBar[i].text and CircularProgressBar[i].value.
I tried to use object array but I can't access the properties of circular progress bar within the for loop, what I also tried is to make to arrays one is type string and it has all the CircularProgressBars.text, and the Other one is the type INT which contains CircularProgressBar.value, but it didn't work, nothing changed in the form.
CircularProgressBar.CircularProgressBar[] cbpArray = new CircularProgressBar.CircularProgressBar[] { shifts1.circularProgressBarNeeShift1, shifts1.circularProgressBarNeeShift2, shifts1.circularProgressBarNeeShift1 };
public Form1()
{
InitializeComponent();
}
image
Okay, since you are trying to reference an item that is in a user control you need to add an accessor inside your UserControl .cs file. I believe it is called shift1.cs for you.
Note: cpb is the name that I gave the CircularProgressBar inside the UserControl shifts1.
public partial class shifts1 : UserControl
{
public CircularProgressBar.CircularProgressBar CPB
{
get
{
return cpb;
}
set
{
cpb = value;
}
}
public shifts1()
{
InitializeComponent();
}
}
Then you will use the name for each user control from your form. In my case I named mine formCPB1 and formCPB2.
CircularProgressBar.CircularProgressBar[] arr = new CircularProgressBar.CircularProgressBar[]
{ formCPB1.CPB, formCPB2.CPB };
Once you have the array you can access them by using the name of the array.
arr[0].Text = "test";
arr[1].Text = "asdf";
I have a main method that has this code:
List<MyType> myList = openDialog();
The call to the openDialog that open a dialog that returns a list with selected items, the dialog is to select items.
private List<MyType> openDialog()
{
MyView myView = new MyView();
MyViewModel myViewModel = new MyViewModel();
myView.DataContext = myViewModel;
myView.ShowDialog();
return myViewModel.Result;
}
myViewModel.Result is a collection that has the selectedItems of the a datagrid in the view.
My question is, how I am returning the Result property of the ViewModel, I am not sure if myViewModel will be recollected by the garbage collector or not, because it still has a reference to it.
To avoid this I am doing this:
private List<MyType> openDialog()
{
MyView myView = new MyView();
MyViewModel myViewModel = new MyViewModel();
myView.DataContext = myViewModel;
myView.ShowDialog();
return new List<MyType>(myViewModel.Result);
}
In the return, I am creating a new list to avoid to reference to the Result property and ensure that the myViewModel object is recollected, but I would like to know if there is a way to avoid to create a new list.
You haven't posted the implementation of MyViewModel but the the List<MyType> returned from the view model won't prevent the view model from being garbage collected. You can confirm this yourself using a WeakReference:
private void Test(object sender, RoutedEventArgs e)
{
Window myView = new Window();
MyViewModel myViewModel = new MyViewModel();
myView.DataContext = myViewModel;
myView.ShowDialog();
List<MyType> result = myViewModel.Result;
WeakReference viewModelWeakReference = new WeakReference(myViewModel);
myView.DataContext = null;
myViewModel = null;
GC.Collect();
GC.WaitForPendingFinalizers();
bool isViewModelAlive = viewModelWeakReference.IsAlive; //=false
}
...
public class MyViewModel
{
public List<MyType> Result { get; } = new List<MyType>() { new MyType(), new MyType() };
}
isViewModelAlive is false and result.Count is 2 when running the above sample code, i.e. the view model has been collected but the List<MyType> remains in memory.
Note that WeakReference.IsAlive will return true when you hold a strong reference to the method that performs the GC test:
List<MyType> _result;
private void Button_Click(object sender, RoutedEventArgs e)
{
_result = openDialog();
}
private List<MyType> openDialog()
{
Window myView = new Window();
MyViewModel myViewModel = new MyViewModel();
myView.DataContext = myViewModel;
myView.ShowDialog();
List<MyType> result = myViewModel.Result;
WeakReference viewModelWeakReference = new WeakReference(myViewModel);
myView.DataContext = null;
myViewModel = null;
GC.Collect();
GC.WaitForPendingFinalizers();
bool isViewModelAlive = viewModelWeakReference.IsAlive;
return result;
}
But myViewModel will still be eligible for garbage collection once the method has returned as it has no GC roots.
So there is no need to create another List<MyType> here.
This question is a little confused. However lets test it out.
Given
public class MyGCCollectClass
{
public List<Version> Garbage { get; set; }
public List<int> MyInnocentList { get; set; }
public List<int> Main()
{
Garbage = new List<Version>();
for (var i = 0; i < 10000000; i++)
{
Garbage.Add(new Version());
}
MyInnocentList = new List<int>();
return MyInnocentList;
}
}
We can look at whats being collected and not
// Lets start from a collect
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Memory used before collection: {0:N0}", GC.GetTotalMemory(false));
// create a class/viewmodel
var gcCollectClass = new MyGCCollectClass();
// create a world of garbage
// return back a list that is part of the class
var list = gcCollectClass.Main();
// lets see whats GC has
Console.WriteLine("Memory used: {0:N0}", GC.GetTotalMemory(true));
// make sure our garbage is still alive
Console.WriteLine(gcCollectClass.Garbage.Count);
// Kill the original class
gcCollectClass = null;
// Force a collection
GC.Collect();
GC.WaitForPendingFinalizers();
// double check the list is still alive
Console.WriteLine(list.Count);
// Lets make sure we havent caused a memory leak
Console.WriteLine("Memory used after full collection: {0:N0}",
GC.GetTotalMemory(true));
Output
Memory used before collection: 30,088
Memory used: 307,138,940
10000000
1
Memory used after full collection: 29,968
The fact is, just because you are returning a list from your class and its part of the class doesn't mean returning it will stop your viewmodal from being cleaned up. It "will" get collected in this situation and there are no memory leaks
Further reading
Why doesn't C# support the return of references?
Memory in .NET - what goes where
The memory slot for a variable is stored on either the stack or the
heap. It depends on the context in which it is declared:
Each local variable (ie one declared in a method) is stored on the stack. That includes reference type variables - the variable itself is
on the stack, but remember that the value of a reference type variable
is only a reference (or null), not the object itself. Method
parameters count as local variables too, but if they are declared with
the ref modifier, they don't get their own slot, but share a slot with
the variable used in the calling code. See my article on parameter
passing for more details.
Instance variables for a reference type are always on the heap. That's where the object itself "lives".
Instance variables for a value type are stored in the same context as the variable that declares the value type. The memory slot for the
instance effectively contains the slots for each field within the
instance. That means (given the previous two points) that a struct
variable declared within a method will always be on the stack, whereas
a struct variable which is an instance field of a class will be on the
heap.
Every static variable is stored on the heap, regardless of whether it's declared within a reference type or a value type. There is only
one slot in total no matter how many instances are created. (There
don't need to be any instances created for that one slot to exist
though.) The details of exactly which heap the variables live on are
complicated, but explained in detail in an MSDN article on the
subject.
https://ericlippert.com/2011/06/23/ref-returns-and-ref-locals/
Whether the object referenced by myViewModel variable can be collected depends on whether any other reachable object references it. In this case, assuming no one else has a reference to it, it depends on whether the object returned by the myViewModel.Result property has any reference (e.g. field, property, delegate) to it. If it doesn't, the copying of the collection isn't necessary. If it does, the copy seem to be an acceptable solution.
Unless the view model uses some limited resources (database connection, file handles, network connections and so on, in which case it should use IDisposable pattern), there's no reason to be overly anxious about its garbage collection.
However, to satisfy one's curiosity, you may test whether the object is GCed using the answer here.
I wanted to understand more about how the ref keyword works so I made the following experiment:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var main = new Main { Property = 1 };
var dependent = new Dependent(main);
void ChangeRef(ref Main Oldmain, Main newMain)
{
Oldmain = newMain;
}
ChangeRef(ref main, new Main { Property = 5 });
Assert.AreEqual(5,dependent.Main.Property);
}
}
public class Main
{
public int Property { get; set; }
}
public class Dependent
{
public Dependent(Main main)
{
Main = main;
}
public Main Main { get; set; }
}
As you can see I was expecting to be able to replace the object that main was referencing while keeping the reference, but the test fails and with the value still being 1. Could someone elaborate abit on why that wasnt working or point me to a place where I can read more?
Update:
Like someone answered below, but later removed.
Why doesnt it work if I pass the main object by reference to the dependent in the constructor?
Shouldnt they all have the same reference?
As others have pointed, you cannot instantly make all variables and fields in your program point to a different instance.
But if you want to reflect a change in all parts of the program, the simplest way is to wrap it in a different class (like your Dependent class). Then you can share the class with other parts of the program, and change its properties instead:
class SomeOtherObject
{
readonly Dependent _dependent;
public Dependent { get { return _dependent; }}
public SomeOtherObject(Dependent dependent)
{
_dependent = dependent;
}
public void Print()
{
Console.WriteLine(_dependent.Main.Property);
}
}
So now you can do this:
var dependent = new Dependent(new Main { Property = 1 });
var someOtherObject = new SomeOtherObject(dependent);
// this will print "1"
someOtherObject.Print();
dependent.Main = new Main { Property = 5; };
// this will print "5"
someOtherObject.Print();
In this case, obviously, simply changing dependent.Main.Property would also do the trick. So, if all parts of your program point to a single object, you can mutate it (i.e. change its internal data) and everyone will see the change, but you cannot make all parts of your program change what they are pointing to.
It's worth noting that you need to be careful when doing this in multithreaded programs; you rarely want some other thread to be able to randomly change your internal data.
That's also why it's best to try to keep your properties readonly, and your objects immutable, if possible.
After doing that
var main = new Main { Property = 1 };
You have object of type Main allocated somewhere in memory (let's name it Main1), at some memory address X, and variable main points to that object. "Points" means it literally stores address of that object Main1, so main contains X.
Then you pass reference to Main1 to the constructor of Dependent object
var dependent = new Dependent(main);
Dependent object is also allocated somewhere in memory, and one of its fields stores reference to Main1 object. So dependent.Main also stores X.
When you do
ChangeRef(ref main, new Main { Property = 5 });
You allocate new object Main5 somewhere at memory address Y. Now you change what address variable main points to. Before it stored address X (address of Main1), now it stores address Y (address of Main5). But dependent.Main still stores address X, because you didn't change it in any way, so it still points to object Main1.
public partial class ThanglishToTamilGUI : Form
{
public string anz;
public ThanglishToTamilGUI()
{
InitializeComponent();
}
public void btnConvertToBraille_Click(object sender, EventArgs e)
{
anz = richTextBoxTamil.Text.ToString();
GUI.TamilToBrailleGUI c1 = new GUI.TamilToBrailleGUI();
c1.Visible = true;
}
}
I need to pass my richtextbox (richTextBoxTamil) content to variable call anz.
I am retrriving anz variable in other form as form load event:
private void TamilToBrailleGUI_Load(object sender, EventArgs e)
{
ThanglishToTamilGUI tt = new ThanglishToTamilGUI();
String apper = tt.anz;
richTextBoxTamil.Text = apper;
}
My Problem:
I am getting null values as result. Since if I assigned any values that invoked correctly.
public partial class ThanglishToTamilGUI : Form
{
public string anz = "Hai";
public ThanglishToTamilGUI()
{
InitializeComponent();
} ...
Here my ans value is passed as "Hai". But my requirement is to get what ever the content in the richTextBoxTamil and pass it to that public variable call anz. What went wrong here please help me.
Thank you.
This is the problem:
ThanglishToTamilGUI tt = new ThanglishToTamilGUI();
String apper = tt.anz;
How do you expect apper to ever be anything other than null? You're fetching the variable from a freshly-created form, which has never been shown, and which has never had btnConvertToBraille_Click called on it.
Presumably there's an existing ThanglishToTamilGUI object somewhere, and that's the one you want to fetch the variable from. Basically, one form needs to know about the instance of the other form.
(I'd also strongly suggest using a property rather than a public variable, but that's a different matter. You might not even need to have a separate variable at all - just declare a property which fetches richTextBoxTamil.Text.)
Alternatively, just pass the relevant string to the constructor of the new form:
public void btnConvertToBraille_Click(object sender, EventArgs e)
{
GUI.TamilToBrailleGUI c1 = new GUI.TamilToBrailleGUI(richTextBoxTamil.Text);
c1.Visible = true;
}
Then the new form doesn't need to know about the old form at all - it only needs to know the text to display.
(You might want to pull it out of the constructor and into a settable property, but it's the same basically principle: the code creating the form pushes the data, rather than the new form pulling it.)
You can create a public property to access the current Text value of the textbox.
public string RichTextBoxText
{
get
{
return richTextBoxTamil.Text;
}
}
The way you do it now the form is instantiated, but the click event is not fired. So there's no way you will get anything other than what you initialized the field to.
Load is not the place to look for user input. An event (like click) is where you need to check the property value:
private void SomeClick(object sender, EventArgs e)
{
String result = thanglishToTamilGUIObject.RichTextBoxText;
//do something with text
}
My program class has:
Application.Run(new Form1());
in form1 class I have:
model = new Model(this);
modelarray myArray = new modelarray(this);
model = myArray.models[0];
myArray.models[1] = (Model) model.Clone();
private void btn13_Click(object sender, EventArgs e)
{
model.btn13Clicked();
}
private void btnGetBackClone_Click(object sender, EventArgs e)
{
model = myArray.models[1];
//here I'm expecting to get the original object back (ie. with btns[7,7].Visible = True) but it doesn't work!!
}
in model class I have:
private Button[,] btns;
public Model(Form1 form1)
{
btns = new Button[10,10];
myform = form1;
btns[8, 6] = form1.btn1;
btns[9, 5] = form1.btn2;
btns[7, 7] = form1.btn13;
}
public void btn13Clicked()
{
btns[7, 7].Visible = False;
}
public object Clone()
{
return this.MemberwiseClone();
}
in modelarray class I have:
public Model[] models = new Model[19];
public modelarray(Form1 form1)
{
models[0] = new Model(form1);
}
Note my comment under the btnGetBackClone_Click method.
"//here I'm expecting to get the original object back (ie. with btns[7,7].Visible = True) but it doesn't work!!"
I understand that this is because models[0] and models[1] are pointing to the same memory location (ie copy by ref). But I am really lost at how to implement a solution in this situation. Searches on 'deep copy' did not seem to help as serializing a form didn't work. Can someone please correct my cloning error?
I know I could simply redo "btns[7, 7].Visible = True;" but I would like to know a cloning solution so it will copy all future fields I decide to put in my model.
I've had a search on codeproject.etc but there doesn't seem to be any straightforward intro to winforms.
.NET usually uses shallow copies during Clone operations.
In order to implement deep copies, you typically have 2 options
Serialize / deserialize (if your classes are all serializable) - e.g. Here
By using reflection e.g. Here
If you split your data (model) concerns out of your form (view), you can then more easily 'clone' just the data.