Just trying to figure out an easy way to either pass or share some data between the main window and a dialog box.
I've got a collection of variables in my main window that I want to pass to a dialog box so that they can be edited.
The way I've done it now, is I pass in the list to the constructor of the dialog box:
private void Button_Click(object sender, RoutedEventArgs e)
{
var window = new VariablesWindow(_templateVariables);
window.Owner = this;
window.ShowDialog();
if(window.DialogResult == true)
_templateVariables = new List<Variable>(window.Variables);
}
And then in there, I guess I need to deep-copy the list,
public partial class VariablesWindow : Window
{
public ObservableCollection<Variable> Variables { get; set; }
public VariablesWindow(IEnumerable<Variable> vars)
{
Variables = new ObservableCollection<Variable>(vars);
// ...
So that when they're edited, it doesn't get reflected back in the main window until the user actually hits "Save".
Is that the correct approach? If so, is there an easy way to deep-copy an ObservableCollection? Because as it stands now, I think my Variables are being modified because it's only doing a shallow-copy.
I think you are indeed following the right approach here, but you need to make a deep copy of your ObservableCollection. To do so, make sure that your class 'Variable' is Clonable (try to implement ICloneable)
foreach(var item in vars)
{
Variables.Add((Variable)item.Clone());
}
I would use events to communicate between the two forms if you want the main form to update while the dialog is open. Expose an event ("ItemAdded" or whatever) from your dialog class that the main form can handle. When that event is fired, update the main form as needed.
This extension method might help somebody:
public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> collection) where T : ICloneable
{
return collection.Select(x => (T) x.Clone());
}
It simplifies my dialog window slightly:
public partial class VariablesWindow : Window
{
public ObservableCollection<TemplateVariable> Variables { get; private set; }
public VariablesWindow(IEnumerable<TemplateVariable> vars)
{
Variables = new ObservableCollection<TemplateVariable>(vars.DeepCopy());
Related
Very new to WPF. I am trying to achieve something relatively simple that is proving to be difficult.
Basically, I want to add an Item to my List Box. The List Box is created in my LiveView xaml/class, but I want to update the contents of the List Box when I push a button in my SettingsView Class.
SettingsView class:
public partial class SettingsView : UserControl
{
public SettingsView()
{
InitializeComponent();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
var myLiveView = new LiveView();
myLiveView.updateListBox();
}
}
LiveView class:
public partial class LiveView : UserControl
{
public LiveView()
{
InitializeComponent();
}
public void updateListBox()
{
CommentListBox.Items.Add("Another item");
}
}
If I do the following, the code works and an item is sucessfully added to my list on startup.
public partial class LiveView : UserControl
{
public LiveView()
{
InitializeComponent();
CommentListBox.Items.Add("Another item");
}
}
Why can I only update the UI inside the Liveview class()? What is the right way to go about this? how can I update my ListBox from another class/view? The instance that I'm creating of LiveView doesn't appear to actually do anything. Any help would be much appreiated, thank you.
When you create a new instance of user control so it is empty now, to handle this please check the delegates and events in both the user controls with respect parent and child navigation.
Fixed using MVVM which made this whole process much easier.
I was wondering how you would close the Form that is currently in focus or the one which a control is contained in. For example, I have an imported header with a menu that I import into all forms in my application.
This is the (simplified) code in my Header class:
public static Panel GetHeader()
{
...
menuItem.Text = "Menu Item";
menuItem.Name = "Next form to open";
menuItem.Click += toolStrip_Click;
...
}
public static void toolStrip_Click(object sender, EventArgs e)
{
ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
NavigationClass.SaveNextForm(menuItem.Name);
}
The navigation class is just something I made which will select the next form to open but I couldn't find anything to then close the current one (since Close() isn't an option due to it being imported with Controls.Add(HeaderClass.GetHeader))
Edit
Just to make clear, this form is in another file which is just a normal class file. That's where the difficulty lies because I'm trying to avoid a severe violation of the DRY principle
Don't use static handlers as #Hans Passant suggests. That is important.
Try sending your main form to your class as a parameter, and store it in that class. This can be done either when you are instantiating your class, or after that. Then, when you need to close the form, call it's Close method. Since you don't include your codes in more details, here is my example with some assumptions.
public class MainForm : Form
{
private HeaderClass HeaderClass;
public MainForm()
{
HeaderClass = new HeaderClass(this);
}
}
public class HeaderClass
{
private MainForm MainForm;
public HeaderClass(MainForm mainForm)
{
MainForm = mainForm;
}
public void MethodThatYouNeedToCloseTheFormFrom()
{
...
MainForm.Close();
...
}
}
Let us know if you require any more elaboration.
I'm trying to call a function in a main form from another form... Already got to call a simple function, by declaring it public static in main form, yet I can't call the needed one.
The function to call:
public static void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
MainForm.txtSendKeys.Text = dial;// Here it asks me for a reference to an object.
foreach (char c in txtSendKeys.Text)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
txtSendKeys.Clear();
}
The procedure I use to call it from a child form:
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Hoho";
MainForm.spotcall();
}
I completely admit that I lack some theory about C#, but as it often happens, I just have to do it for my work, so I expect to get help if by chance I don't get to the solution by myself. Thank you :)
You cannot reference instances of controls on your MainForm in a static method. Like the compiler is telling you, you need an instance of the form in order to update things like TextBoxes. Without an instance, where would the values you are trying to update go?
I'm not sure exactly how the child form is being created, but one way you could call methods on your MainForm would be to provide a reference to your MainForm instance directly to the child form. This could be through the constructor or some public property.
For example
public class ChildForm : Form {
public MainForm MyParent { get; set; }
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Hoho";
// Now your child can access the instance of MainForm directly
this.MyParent.spotcall();
}
}
Assuming you are creating ChildForm inside of MainForm the code to give the child a reference is pretty simple:
var childForm = new ChildForm();
childForm.MyParent = this; // this is a `MainForm` in this case
childForm.Show();
You would also need to make spotcall an instance method and not a static method, and remove the static reference to MainForm in your code:
public void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
// Now it no longer asks you for a reference, you have one!
txtSendKeys.Text = dial;
foreach (char c in txtSendKeys.Text)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
txtSendKeys.Clear();
}
I think the correct way to do this is to use delegates. This way your form (window) does not have to know anything about the parent form (the form can be opened from different parent forms).
Let's say we want to call a function in the parent form when the child form is closed (not showing the form as modal).
At the top of your child form create a delegate:
public delegate void CloseEvent();
public CloseEvent WindowClosed;
Create the form closing event and have it call your delegate:
private void child_FormClosing(object sender, FormClosingEventArgs e)
{
WindowClosed();
}
A button in the parent form can show the child form and set the callback:
private ChildForm childform = null;
private void buttonShowChildForm_Click(object sender, EventArgs e)
{
if (childform == null)
{
childform = new ChildForm();
childform.WindowClosed += childClosed;
childform.Show();
} else
{
childform.BringToFront();
}
}
private void childClosed()
{
childform = null;
}
In this example we use a button to open a new form that does not block the parent form. If the user tries to open the form a second time, we just bring the existing form to the front to show it to the user. When the form is closed we set the object to null so that next time we click the button a new form is opened because the old was disposed when closed.
Best regards
Hans Milling...
You can not access non-static members in static context, which means you have to made txtSendKeys static, or make your function non-static.
If you create a static function, you may not reference global variables inside the function that aren't static as well.
So in order for spotcall to be static, you have to remove the reference to the txtSendKeys (I'm assuming this is a text box that you have created elsewhere in the form) or txtSendKeys must be declared within the static function.
Additional:
You obtained the value for txtSendKeys.Text in the previous line, via variable dial. Instead of referencing txtSendKeys.Text at all, I imagine you could simply use the variable dial to complete the function and leave the function static (you clear it at the end anyway).
public static void spotcall()
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
foreach (char c in dial)
{
sideapp.Keyboard.SendKey(c.ToString(), checkBoxPrivate.Checked);
}
}
Although, that wouldn't overcome the same issue you would likely run into with checkBoxPrivate.Checked.
You could change it to take a boolean argument.
public static void spotcall(Boolean PrivateChecked)
{
string dial = Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("INTERCOMCS").GetValue("DIAL").ToString();
foreach (char c in dial)
{
sideapp.Keyboard.SendKey(c.ToString(), PrivateChecked);
}
}
You can put the shared code in a third class that's visible to both forms. So, for example:
public class static HelperFunctions
{
public static void spotcall()
{
. . .
}
}
Then replace
MainForm.spotcall()
with
HelperFunctions.spotcall()
The MainForm is just a class. It has the structure of the class. But the only data you can get from it is static data.
But an instance of that class appears when you do: MainForm MyFormInstance = new MainForm();
The MainForm can be used only to access static members (methods, properties...).
When you want to get the txtSendKeys, you must get it from an instance (object reference). That's because the textbox is not static, so it only exists in instances of the form.
So, you should do the following:
Make spotcall NOT static.
Put in child form a variable MainForm MyParentMainForm;
When you call the child, set that MyParentMainForm with the instance of the mainform. If it's being called from the main form, you can get the instance with the this keyword.
Inside child form, call MyParentMainForm.spotcall
PS: I'm not sure if there's something like a real child form or if you just call new forms from another. If there's really a child form, you can get the Parent property to access the instance of the main form.
This is sort of a "design pattern" issue, which I'll elaborate on, but I can try to explain the most direct way to solve this if you don't expect this program to change very much. "Static" things only exist once - once in the entire application. When a variable or function is static, it's much easier to access from anywhere in the program; but you can't access an object's associated data, because you're not pointing to a particular instance of that object (ie, you have seven MainForms. Which one are you calling this function on?) Since standard WinForm design expects you could have seven copies of MainForm displaying, all variables associated are going to be instance variables, or non-static. However, if you expect never to have a second MainForm, then you can take the "singleton" approach, and have an easy way of accessing your one instance.
partial class MainForm {
// only including the code that I'm adding; I'm sure there's a lot of stuff in your form.
public static MainForm Instance { public get; private set; }
protected void onInitialize() { // You need to hook this part up yourself.
Instance = this;
}
}
partial class SubForm {
protected void onImportantButton() {
MainForm.Instance.doImportantThing()
}
}
Putting too much active data-changing logic in form classes is a pretty common issue with many beginners' code. That's not a horrible thing - you wouldn't want to be making 5 controlling classes just for a simple thing you're trying. As code gets more complex, you start to find some things would make more sense to move to a "sublevel" of classes that don't interact with the user (so, some day, if this is being re-coded as a server program, you could throw away the form classes, and just use the logic classes - theoretically speaking). It also takes some time for many programmers to understand the whole concept of object "instances", and the "context" that a function is called in.
I have done some research on this question before deciding to ask it. I just could not find anything that helped me.
I am writing an application in C# for the compact framework 2.0.
I need to take a data object instantiated on form1 and pass that object a form2. Work on the data object in form2 and then pass that data back to form1 so it can be saved.
I understand that a form is just an object an I also understand that objects are past by reference and not by value. I also understand the difference between the two types. I just cannot make it work for some reason.
What is the best, and cleanest, way in code to achieve this?
What you need to do is create a second constructor to your second form that accepts an object as a parameter... for all I care, it could be the entire Form1 object instance, then you can get whatever you want from it. Preserve this object in your second form and modify it as needed there. Upon completion of your second form, your first form will have that data and you can do whatever "refreshing" once the second form closes.
public partial class YourSecondForm : Form
{
object PreserveFromFirstForm;
public YourSecondForm()
{
... its default Constructor...
}
public YourSecondForm( object ParmFromFirstForm ) : this()
{
this.PreserveFromFirstForm = ParmFromFirstForm;
}
private void YourSecondFormMethodToManipulate()
{
// you would obviously have to type-cast the object as needed
// but could manipulate whatever you needed for the duration of the second form.
this.PreserveFromFirstForm.Whatever = "something";
}
}
I've always liked the eventing model for this. This way your forms don't need to know about anyone else. You can setup an event like the following in some kind of EventHandler class that is used by both forms.
public delegate void SavingObjectHandler(MyObject obj);
public event SavingObjectHandler SavingObjectEvent;
public void SavingObject(MyObject obj)
{
if (SavingObjectEvent != null) SavingObjectEvent(obj);
}
then your one form can call the SavingObject even handler and the other can subscribe to the SavingObjectEvent. When the first form triggers the event the second form will be notified do the processing that it needs and the object will then be available to the first form again after the manipulation.
Something like this where the ObservableForm is a base class and contains the ChangeEvent for further flexability:
public class FormMain : Form
{
private ObServableForm childForm = null;
public FormMain () {
this.childForm = new ObservableFormConcreateA(this);
this.childForm.ChangeEvent += (sender, e) => Application.DoEvents();
}
public void Present() {
this.childForm.Show();
}
}
public class ObservableFormConcreateA ObServableForm
{
private Form workItemForm = null;
private delegate void FormChangedHandler(object source, EventArgs args);
//ToDo: this should go in the superclass
public event FormChangedHandler ChangeEvent;
public FormChild(ObServableFormworkItem) {
this.workItemForm = workItem;
}
public void OnUserActionHandler(object sender, EventArgs e) {
this.formItemForm.Property = this.txtBoxWhateverValue.Text;
if(ChangeEvent != null)
ChangeEvent(this, //create your args to specify which control/data changes);
}
}
I've got an interesting solution for you, involving closure. In the constructor for Form2, require an Action<TypeOfThing> object, and whenever you need to return the data to Form1, call that Action and pass the data into it. For example:
class Form1 : Form
{
private void SomeFunction()
{
TypeOfData data;
Form2 form2 = new Form2((d) => { data = d; });
form2.ShowDialog() // or whatever you do with form2
// After you've definitely got your data object from Form2
DoStuff(data);
}
}
class Form2 : Form
{
private Action<TypeOfData> returnData;
private TypeOfData data;
public Form2(Action<TypeOfData> r)
{
returnData = r;
}
private void SomeFunction()
{
// Whenever it comes time to return the data you've collected
returnData(data);
}
}
I've used this implementation in the following circumstance: I had to request a password from the user, and I wanted to do so with a dialog box, so I designed my dialog box with a textbox where the user could type their password, and OK and Cancel buttons. On FormClosing, I'd return the string (their password) by calling the Action, and I would only ever use that Form as a dialog, so I could be sure the variable would be assigned to a string by the time the code continued in Form1. This way I didn't have to make a property for Password, which wouldn't have made sense because the password was only a temporarily necessary piece of data.
How can I make a textbox in my winforms application that accepts new lines of text from anywhere in the application?
I have a main form that contains a textbox. I'd like to directly add text to the box from a method in another class.
Update
I tried this in my main form:
public void Output(String value)
{
if (txtOutput.Text.Length > 0)
{
txtOutput.AppendText(Environment.NewLine);
}
txtOutput.AppendText(value);
}
But I can't call Output from the other class. I'm new to C#, so perhaps I'm missing something obvious.
Regards, Miel.
PS Yes, I know this is bad design, but for now this seems to be the best way to do what I want. The textbox would function like a console.
You'll need to expose the Text property of the TextBox as a string property on your form. For example...
public string TextBoxText
{
get { return textBoxName.Text; }
set { textBoxName.Text = value; }
}
Edit
After reading the question edit, your problem is that you need a reference to a specific instance of the form whereever you're trying to execute that code. You can either pass around a reference (which is the better option), or you could use some smelly code and have a static property that refers to one instance of your form. Something like...
public partial class MyForm : Form
{
private static MyForm instance;
public static MyForm Instance
{
get { return instance; }
}
public MyForm() : base()
{
InitializeComponent();
// ....
instance = this;
}
}
Using this approach, you could call MyForm.Instance.Output("test");
In order to decouple a bit more you could inverse the control a bit:
// interface for exposing append method
public interface IAppend
{
void AppendText(string text);
}
// some class that can use the IAppend interface
public class SomeOtherClass
{
private IAppend _appendTarget = null;
public SomeOtherClass(IAppend appendTarget)
{
_appendTarget = appendTarget;
}
private void AppendText(string text)
{
if (_appendTarget != null)
{
_appendTarget.AppendText(text);
}
}
public void MethodThatWillWantToAppendText()
{
// do some stuff
this.AppendText("I will add this.");
}
}
// implementation of IAppend in the form
void IAppend.AppendText(string text)
{
textBox1.AppendText(text);
}
It looks like your design is a little bit corrupted. You shouldn't let buisness logic mess with GUI controls. Why don't you try a return value and assigning it on the interface side?
This is a REALLY bad way of doing it, but just to make sure all the answers are out there...
In the VS designer, each form control has an item in the Properties window named Modifiers that defaults to Private. Changing this to one of the others settings, such as Internal or Public, will let you access it from outside the form.
I must stress that this is the worst way to do it.